Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions subscription_oca/models/sale_subscription_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,33 @@ class SaleSubscriptionLine(models.Model):
readonly=False,
)

@api.depends("product_id", "price_unit", "product_uom_qty", "discount", "tax_ids")
sequence = fields.Integer(default=10, help="Display order on the subscription.")
display_type = fields.Selection(
selection=[("line_section", "Section"), ("line_note", "Note")],
default=False,
help="Technical field for UX purpose.",
)

@api.depends(
"product_id",
"price_unit",
"product_uom_qty",
"discount",
"tax_ids",
"display_type",
)
def _compute_subtotal(self):
for record in self:
if record.display_type:
record.update(
{
"amount_tax_line_amount": 0.0,
"price_total": 0.0,
"price_subtotal": 0.0,
}
)
continue

price = record.price_unit * (1 - (record.discount or 0.0) / 100.0)
taxes = record.tax_ids.compute_all(
price,
Expand Down Expand Up @@ -80,9 +104,11 @@ def _compute_subtotal(self):
index=True,
)

@api.depends("product_id")
@api.depends("product_id", "display_type")
def _compute_name(self):
for record in self:
if record.display_type:
continue
if not record.product_id:
record.name = False
lang = get_lang(self.env, record.sale_subscription_id.partner_id.lang).code
Expand All @@ -91,9 +117,15 @@ def _compute_name(self):
lang=lang
).get_product_multiline_description_sale()

@api.depends("product_id", "sale_subscription_id.fiscal_position_id")
@api.depends(
"product_id", "display_type", "sale_subscription_id.fiscal_position_id"
)
def _compute_tax_ids(self):
for line in self:
if line.display_type:
line.tax_ids = [(5, 0, 0)]
continue

fpos = (
line.sale_subscription_id.fiscal_position_id
or line.sale_subscription_id.fiscal_position_id._get_fiscal_position(
Expand All @@ -110,9 +142,13 @@ def _compute_tax_ids(self):
"product_id",
"sale_subscription_id.partner_id",
"sale_subscription_id.pricelist_id",
"display_type",
)
def _compute_price_unit(self):
for record in self:
if record.display_type:
record.price_unit = 0.0
continue
if not record.product_id:
continue
if (
Expand Down Expand Up @@ -143,9 +179,13 @@ def _compute_price_unit(self):
"tax_ids",
"sale_subscription_id.partner_id",
"sale_subscription_id.pricelist_id",
"display_type",
)
def _compute_discount(self):
for record in self:
if record.display_type:
record.discount = 0.0
continue
if not (
record.product_id
and record.product_id.uom_id
Expand Down Expand Up @@ -289,6 +329,12 @@ def _get_display_price(self, product):

def _prepare_sale_order_line(self):
self.ensure_one()
if self.display_type:
return {
"display_type": self.display_type,
"name": self.name or "",
"sequence": self.sequence,
}
return {
"product_id": self.product_id.id,
"name": self.name,
Expand All @@ -298,10 +344,17 @@ def _prepare_sale_order_line(self):
"price_subtotal": self.price_subtotal,
"tax_id": self.tax_ids,
"product_uom": self.product_id.uom_id.id,
"sequence": self.sequence,
}

def _prepare_account_move_line(self):
self.ensure_one()
if self.display_type:
return {
"display_type": self.display_type,
"name": self.name or "",
"sequence": self.sequence,
}
account = (
self.product_id.property_account_income_id
or self.product_id.categ_id.property_account_income_categ_id
Expand All @@ -316,4 +369,5 @@ def _prepare_account_move_line(self):
"tax_ids": [(6, 0, self.tax_ids.ids)],
"product_uom_id": self.product_id.uom_id.id,
"account_id": account.id,
"sequence": self.sequence,
}
118 changes: 118 additions & 0 deletions subscription_oca/tests/test_subscription_oca.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,3 +691,121 @@ def _collect_all_sub_test_results(self, subscription):
)
test_res.append(group_stage_ids)
return test_res

def test_display_type_section_line_computes_and_prepares(self):
# Selection line
line = self.env["sale.subscription.line"].create(
{
"company_id": 1,
"sale_subscription_id": self.sub1.id,
"display_type": "line_section",
"name": "Plans and prices",
"sequence": 5,
}
)

# Comptes: must be all zero or empty
self.assertEqual(line.price_unit, 0.0)
self.assertEqual(line.discount, 0.0)
self.assertEqual(line.price_subtotal, 0.0)
self.assertEqual(line.price_total, 0.0)
self.assertEqual(line.amount_tax_line_amount, 0.0)
self.assertFalse(line.tax_ids, "tax_ids must be empty with display_type")

# The name should not be overwritten by the product (it keeps what we set)
self.assertEqual(line.name, "Plans and prices")

# Prepare values: only display fields
so_vals = line._prepare_sale_order_line()
self.assertEqual(so_vals.get("display_type"), "line_section")
self.assertEqual(so_vals.get("name"), "Plans and prices")
self.assertEqual(so_vals.get("sequence"), 5)

# It should not include product/price keys
self.assertNotIn("product_id", so_vals)
self.assertNotIn("price_unit", so_vals)
self.assertNotIn("discount", so_vals)
self.assertNotIn("tax_id", so_vals)

aml_vals = line._prepare_account_move_line()
self.assertEqual(aml_vals.get("display_type"), "line_section")
self.assertEqual(aml_vals.get("name"), "Plans and prices")
self.assertEqual(aml_vals.get("sequence"), 5)
self.assertNotIn("product_id", aml_vals)
self.assertNotIn("price_unit", aml_vals)
self.assertNotIn("discount", aml_vals)
self.assertNotIn("tax_ids", aml_vals)

def test_display_type_note_line_computes_and_prepares(self):
# Note line
line = self.env["sale.subscription.line"].create(
{
"company_id": 1,
"sale_subscription_id": self.sub1.id,
"display_type": "line_note",
"name": "Note: discount applicable from the 2nd year",
"sequence": 15,
}
)
# Computes: zero / empty
self.assertEqual(line.price_unit, 0.0)
self.assertEqual(line.discount, 0.0)
self.assertEqual(line.price_subtotal, 0.0)
self.assertEqual(line.price_total, 0.0)
self.assertEqual(line.amount_tax_line_amount, 0.0)
self.assertFalse(line.tax_ids)

# Prepare values: only display fields
so_vals = line._prepare_sale_order_line()
self.assertEqual(so_vals.get("display_type"), "line_note")
self.assertEqual(
so_vals.get("name"), "Note: discount applicable from the 2nd year"
)
self.assertEqual(so_vals.get("sequence"), 15)
self.assertNotIn("product_id", so_vals)
self.assertNotIn("price_unit", so_vals)
self.assertNotIn("discount", so_vals)
self.assertNotIn("tax_id", so_vals)

aml_vals = line._prepare_account_move_line()
self.assertEqual(aml_vals.get("display_type"), "line_note")
self.assertEqual(
aml_vals.get("name"), "Note: discount applicable from the 2nd year"
)
self.assertEqual(aml_vals.get("sequence"), 15)
self.assertNotIn("product_id", aml_vals)
self.assertNotIn("price_unit", aml_vals)
self.assertNotIn("discount", aml_vals)
self.assertNotIn("tax_ids", aml_vals)

def test_display_type_toggle_from_normal_line(self):
# Start with a normal line (with product) so that there are imports > 0
line = self.create_sub_line(self.sub1, self.product_1.id)
self.assertGreater(line.price_subtotal, 0.0)
self.assertTrue(line.tax_ids)

# Now we convert it to a display line (note or section)
line.display_type = "line_note"
# Computes must be all zero/empty
self.assertEqual(line.price_unit, 0.0)
self.assertEqual(line.discount, 0.0)
self.assertEqual(line.price_subtotal, 0.0)
self.assertEqual(line.price_total, 0.0)
self.assertEqual(line.amount_tax_line_amount, 0.0)
self.assertFalse(line.tax_ids)

# Prepare vals have to be display
so_vals = line._prepare_sale_order_line()
self.assertEqual(so_vals.get("display_type"), "line_note")
self.assertIn("name", so_vals)
self.assertNotIn("product_id", so_vals)
self.assertNotIn("price_unit", so_vals)
self.assertNotIn("discount", so_vals)

aml_vals = line._prepare_account_move_line()
self.assertEqual(aml_vals.get("display_type"), "line_note")
self.assertIn("name", aml_vals)
self.assertNotIn("product_id", aml_vals)
self.assertNotIn("price_unit", aml_vals)
self.assertNotIn("discount", aml_vals)
self.assertNotIn("tax_ids", aml_vals)
109 changes: 102 additions & 7 deletions subscription_oca/views/sale_subscription_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,19 +118,58 @@
string="Subscription lines"
name="subscription_lines_page"
>
<field name="sale_subscription_line_ids">
<field
name="sale_subscription_line_ids"
widget="section_and_note_one2many"
>
<tree editable="bottom">
<field name="product_id" required="True" />
<control>
<create string="Add a line" />
<create
string="Add a section"
context="{'default_display_type': 'line_section'}"
/>
<create
string="Add a note"
context="{'default_display_type': 'line_note'}"
/>
</control>

<field name="display_type" invisible="1" />
<field name="sequence" widget="handle" />

<field
name="product_id"
attrs="{'required': [('display_type','=',False)], 'readonly': [('display_type','!=',False)]}"
/>

<field
name="name"
required="True"
widget="section_and_note_text"
attrs="{'required': [('display_type','=',False)]}"
/>

<field name="currency_id" invisible="1" />
<field name="product_uom_qty" required="True" />
<field name="price_unit" required="True" />
<field name="discount" required="True" />
<field name="tax_ids" widget="many2many_tags" />

<field
name="product_uom_qty"
attrs="{'invisible': [('display_type','!=', False)]}"
/>
<field
name="price_unit"
attrs="{'invisible': [('display_type','!=', False)]}"
/>
<field
name="discount"
attrs="{'invisible': [('display_type','!=', False)]}"
/>

<field
name="tax_ids"
widget="many2many_tags"
attrs="{'invisible': [('display_type','!=', False)]}"
/>

<field
name="price_subtotal"
options="{'currency_field': 'currency_id'}"
Expand Down Expand Up @@ -206,6 +245,43 @@
</field>
</record>

<record id="sale_subscription_line_form" model="ir.ui.view">
<field name="name">sale.subscription.line.form</field>
<field name="model">sale.subscription.line</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="display_type" invisible="1" />
<group attrs="{'invisible': [('display_type','!=', False)]}">
<field name="product_id" required="1" />
<field name="product_uom_qty" />
<field name="price_unit" />
<field name="discount" />
<field name="tax_ids" widget="many2many_tags" />
</group>

<label
for="name"
string="Description"
attrs="{'invisible': [('display_type','!=', False)]}"
/>
<label
for="name"
string="Section"
attrs="{'invisible': [('display_type','!=','line_section')]}"
/>
<label
for="name"
string="Note"
attrs="{'invisible': [('display_type','!=','line_note')]}"
/>
<field name="name" nolabel="1" />

</sheet>
</form>
</field>
</record>

<record id="sale_subscription_tree" model="ir.ui.view">
<field name="name">sale.subscription.tree</field>
<field name="model">sale.subscription</field>
Expand Down Expand Up @@ -389,6 +465,25 @@
string="Next invoice date"
date="recurring_next_date"
/>

<filter
name="in_progress"
string="In progress"
domain="[('in_progress','=', True)]"
/>

<filter
name="expired"
string="Expired"
domain="[('active','=', True), ('date','!=', False), ('date','&lt;', context_today().strftime('%Y-%m-%d'))]"
/>

<filter
name="inactive"
string="Archived"
domain="[('active','=', False)]"
/>

<group expand="0" string="Group By">
<filter
string="Partner"
Expand Down