changeset 3ddf6d1eab0d in modules/sale_subscription:default details: https://hg.tryton.org/modules/sale_subscription?cmd=changeset;node=3ddf6d1eab0d description: Allow to finish line before next consumption
issue8085 review72401002 diffstat: CHANGELOG | 1 + subscription.py | 46 ++++-- tests/scenario_sale_subscription_new_line.rst | 164 ++++++++++++++++++++++++++ tests/test_sale_subscription.py | 5 + view/subscription_line_form.xml | 14 +- 5 files changed, 209 insertions(+), 21 deletions(-) diffs (354 lines): diff -r 76c74774f819 -r 3ddf6d1eab0d CHANGELOG --- a/CHANGELOG Sat Sep 28 23:59:10 2019 +0200 +++ b/CHANGELOG Mon Oct 14 00:17:45 2019 +0200 @@ -1,3 +1,4 @@ +* Allow lines to be finished before next consumption * Allow services to be deactivated Version 5.2.0 - 2019-05-06 diff -r 76c74774f819 -r 3ddf6d1eab0d subscription.py --- a/subscription.py Sat Sep 28 23:59:10 2019 +0200 +++ b/subscription.py Mon Oct 14 00:17:45 2019 +0200 @@ -5,7 +5,7 @@ from itertools import groupby from sql import operators, Literal, Null -from sql.conditionals import Coalesce +from sql.conditionals import Coalesce, Case from trytond import backend from trytond.i18n import gettext @@ -279,7 +279,7 @@ subscription.compute_next_invoice_date()) for line in subscription.lines: if (line.next_consumption_date is None - and not line.consumed): + and not line.consumed_until): line.next_consumption_date = ( line.compute_next_consumption_date()) lines.extend(subscription.lines) @@ -465,7 +465,7 @@ next_consumption_date_delayed = fields.Function( fields.Date("Next Consumption Delayed"), 'get_next_consumption_date_delayed') - consumed = fields.Boolean("Consumed") + consumed_until = fields.Date("Consumed until", readonly=True) start_date = fields.Date( "Start Date", required=True, domain=[ @@ -473,9 +473,10 @@ ], states={ 'readonly': ((Eval('subscription_state') != 'draft') - | Eval('consumed')), + | Eval('consumed_until')), }, - depends=['subscription_start_date', 'subscription_state', 'consumed']) + depends=[ + 'subscription_start_date', 'subscription_state', 'consumed_until']) end_date = fields.Date( "End Date", domain=['OR', [ @@ -483,18 +484,18 @@ If(Bool(Eval('subscription_end_date')), ('end_date', '<=', Eval('subscription_end_date')), ()), - If(Bool(Eval('next_consumption_date')), - ('end_date', '>=', Eval('next_consumption_date')), + If(Bool(Eval('consumed_until')), + ('end_date', '>=', Eval('consumed_until')), ()), ], ('end_date', '=', None), ], states={ 'readonly': ((Eval('subscription_state') != 'draft') - | (~Eval('next_consumption_date') & Eval('consumed'))), + | (~Eval('consumed_until') & Eval('consumed_until'))), }, depends=['subscription_end_date', 'start_date', - 'next_consumption_date', 'subscription_state', 'consumed']) + 'consumed_until', 'subscription_state', 'consumed_until']) @classmethod def __register__(cls, module): @@ -523,6 +524,15 @@ # Migration from 4.8: drop required on description table_h.not_null_action('description', action='remove') + # Migration from 5.2: replace consumed by consumed_until + if table_h.column_exist('consumed'): + cursor.execute(*table.update( + [table.consumed_until], + [Case((table.consumed, Coalesce( + table.next_consumption_date, + table.end_date)), else_=Null)])) + table_h.drop_column('consumed') + @fields.depends('subscription', '_parent_subscription.state') def on_change_with_subscription_state(self, name=None): if self.subscription: @@ -620,10 +630,6 @@ return self.next_consumption_date + self.consumption_delay return self.next_consumption_date - @classmethod - def default_consumed(cls): - return False - def get_rec_name(self, name): return '%s @ %s' % (self.service.rec_name, self.subscription.rec_name) @@ -675,11 +681,16 @@ while remainings: lines, remainings = remainings, [] for line in lines: - consumptions.append( - line.get_consumption(line.next_consumption_date)) + consumption = line.get_consumption(line.next_consumption_date) + if consumption: + consumptions.append(consumption) line.next_consumption_date = ( line.compute_next_consumption_date()) - line.consumed = True + if line.next_consumption_date: + line.consumed_until = ( + line.next_consumption_date - datetime.timedelta(1)) + else: + line.consumed_until = line.end_date if line.next_consumption_date is None: subscription_ids.add(line.subscription.id) elif line.get_next_consumption_date_delayed() <= date: @@ -692,7 +703,8 @@ def get_consumption(self, date): pool = Pool() Consumption = pool.get('sale.subscription.line.consumption') - return Consumption(line=self, quantity=self.quantity, date=date) + if date < (self.end_date or datetime.date.max): + return Consumption(line=self, quantity=self.quantity, date=date) def compute_next_consumption_date(self): if not self.consumption_recurrence: diff -r 76c74774f819 -r 3ddf6d1eab0d tests/scenario_sale_subscription_new_line.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/scenario_sale_subscription_new_line.rst Mon Oct 14 00:17:45 2019 +0200 @@ -0,0 +1,164 @@ +=================================== +Sale Subscription New Line Scenario +=================================== + +Imports:: + + >>> import datetime + >>> from decimal import Decimal + >>> from dateutil.relativedelta import relativedelta + >>> from proteus import Model, Wizard + >>> from trytond.tests.tools import activate_modules + >>> from trytond.modules.company.tests.tools import create_company, \ + ... get_company + >>> from trytond.modules.account.tests.tools import create_chart, \ + ... get_accounts + +Install sale_subscription:: + + >>> config = activate_modules('sale_subscription') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + +Create chart of accounts:: + + >>> _ = create_chart(company) + >>> accounts = get_accounts(company) + >>> revenue = accounts['revenue'] + +Create party:: + + >>> Party = Model.get('party.party') + + >>> customer = Party(name='Customer') + >>> customer.save() + +Create subscription recurrence rule sets:: + + >>> RecurrenceRuleSet = Model.get('sale.subscription.recurrence.rule.set') + + >>> monthly = RecurrenceRuleSet(name='Monthly') + >>> rule, = monthly.rules + >>> rule.freq = 'monthly' + >>> rule.interval = 1 + >>> monthly.save() + +Create account category:: + + >>> ProductCategory = Model.get('product.category') + >>> account_category = ProductCategory(name="Account Category") + >>> account_category.accounting = True + >>> account_category.account_revenue = revenue + >>> account_category.save() + +Create subscription service:: + + >>> Service = Model.get('sale.subscription.service') + >>> ProductTemplate = Model.get('product.template') + >>> Uom = Model.get('product.uom') + + >>> unit, = Uom.find([('name', '=', 'Unit')]) + + >>> template = ProductTemplate() + >>> template.name = 'Rental' + >>> template.default_uom = unit + >>> template.type = 'service' + >>> template.list_price = Decimal('10') + >>> template.account_category = account_category + >>> template.save() + >>> product, = template.products + + >>> service = Service() + >>> service.product = product + >>> service.consumption_recurrence = monthly + >>> service.save() + +Subscribe:: + + >>> Subscription = Model.get('sale.subscription') + + >>> subscription = Subscription() + >>> subscription.party = customer + >>> subscription.start_date = datetime.date(2019, 1, 1) + >>> subscription.invoice_recurrence = monthly + >>> line = subscription.lines.new() + >>> line.service = service + >>> line.quantity = 1 + >>> subscription.click('quote') + >>> subscription.state + 'quotation' + >>> subscription.click('run') + >>> subscription.state + 'running' + +Create consumption for next two months:: + + >>> LineConsumption = Model.get('sale.subscription.line.consumption') + + >>> line_consumption_create = Wizard( + ... 'sale.subscription.line.consumption.create') + >>> line_consumption_create.form.date = datetime.date(2019, 2, 1) + >>> line_consumption_create.execute('create_') + + >>> len(LineConsumption.find([])) + 2 + +Create invoice for next two months:: + + >>> Invoice = Model.get('account.invoice') + + >>> create_invoice = Wizard('sale.subscription.create_invoice') + >>> create_invoice.form.date = datetime.date(2019, 2, 1) + >>> create_invoice.execute('create_') + + >>> invoice, = Invoice.find([]) + >>> line, = invoice.lines + >>> line.quantity + 2.0 + >>> line.unit_price + Decimal('10.0000') + +Close subscription:: + + >>> subscription.click('draft') + >>> subscription.state + 'draft' + >>> line, = subscription.lines + >>> line.consumed_until + datetime.date(2019, 2, 28) + >>> line.end_date = datetime.date(2019, 2, 28) + >>> new_line = subscription.lines.new() + >>> new_line.service = service + >>> new_line.quantity = 1 + >>> new_line.start_date = datetime.date(2019, 3, 1) + >>> new_line.unit_price = Decimal('15.00') + >>> subscription.click('quote') + >>> subscription.click('run') + >>> subscription.state + 'running' + + >>> line_consumption_create = Wizard( + ... 'sale.subscription.line.consumption.create') + >>> line_consumption_create.form.date = datetime.date(2019, 3, 1) + >>> line_consumption_create.execute('create_') + + >>> len(LineConsumption.find([])) + 3 + +Create next invoice:: + + >>> Invoice = Model.get('account.invoice') + + >>> create_invoice = Wizard('sale.subscription.create_invoice') + >>> create_invoice.form.date = datetime.date(2019, 3, 1) + >>> create_invoice.execute('create_') + + >>> new_invoice, = Invoice.find([('id', '!=', invoice.id)]) + >>> line, = new_invoice.lines + >>> line.quantity + 1.0 + >>> line.unit_price + Decimal('15.00') diff -r 76c74774f819 -r 3ddf6d1eab0d tests/test_sale_subscription.py --- a/tests/test_sale_subscription.py Sat Sep 28 23:59:10 2019 +0200 +++ b/tests/test_sale_subscription.py Mon Oct 14 00:17:45 2019 +0200 @@ -25,4 +25,9 @@ tearDown=doctest_teardown, encoding='utf-8', checker=doctest_checker, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) + suite.addTests(doctest.DocFileSuite( + 'scenario_sale_subscription_new_line.rst', + tearDown=doctest_teardown, encoding='utf-8', + checker=doctest_checker, + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite diff -r 76c74774f819 -r 3ddf6d1eab0d view/subscription_line_form.xml --- a/view/subscription_line_form.xml Sat Sep 28 23:59:10 2019 +0200 +++ b/view/subscription_line_form.xml Mon Oct 14 00:17:45 2019 +0200 @@ -6,28 +6,34 @@ <field name="subscription"/> <label name="sequence"/> <field name="sequence"/> + <notebook colspan="4"> <page string="General" id="general"> <label name="service"/> <field name="service"/> - <newline/> + <label name="unit_price"/> + <field name="unit_price"/> + <label name="description"/> <field name="description" colspan="3" yexpand="0"/> + <label name="consumption_recurrence"/> <field name="consumption_recurrence"/> <label name="consumption_delay"/> <field name="consumption_delay"/> + <label name="quantity"/> <field name="quantity"/> <label name="unit"/> <field name="unit"/> - <label name="unit_price"/> - <field name="unit_price"/> - <newline/> + <label name="start_date"/> <field name="start_date"/> <label name="end_date"/> <field name="end_date"/> + + <label name="consumed_until"/> + <field name="consumed_until"/> </page> </notebook> </form>