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>

Reply via email to