Allow to finish line before next consumption

issue8085
review72401002
This commit is contained in:
Cédric Krier
2019-10-14 00:17:45 +02:00
parent 17062c99bd
commit dd6b98bfcd
5 changed files with 209 additions and 21 deletions
+1
View File
@@ -1,3 +1,4 @@
* Allow lines to be finished before next consumption
* Allow services to be deactivated
Version 5.2.0 - 2019-05-06
+29 -17
View File
@@ -5,7 +5,7 @@ from decimal import Decimal
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 @@ class Subscription(Workflow, ModelSQL, ModelView):
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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
],
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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
# 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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
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 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
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:
@@ -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')
+5
View File
@@ -25,4 +25,9 @@ def suite():
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
+10 -4
View File
@@ -6,28 +6,34 @@ this repository contains the full copyright notices and license terms. -->
<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>