Allow to finish line before next consumption
issue8085 review72401002
This commit is contained in:
@@ -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
@@ -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')
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user