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
77 changes: 77 additions & 0 deletions sale_section_and_note_revamp/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
============================
Sale Section and Note Revamp
============================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:93a18de79802b53ca002dd70a96fbae9f23b29c5ede4e2a1c0c2722b83e76095
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--reporting-lightgray.png?logo=github
:target: https://github.com/OCA/sale-reporting/tree/15.0/sale_section_and_note_revamp
:alt: OCA/sale-reporting
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/sale-reporting-15-0/sale-reporting-15-0-sale_section_and_note_revamp
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/sale-reporting&target_branch=15.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module provides high level method helpers to ease sale sections and notes
management and use elsewhere.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-reporting/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/sale-reporting/issues/new?body=module:%20sale_section_and_note_revamp%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Camptocamp

Contributors
~~~~~~~~~~~~

* Stéphane Mangin <stephane.mangin@camptocamp.com>

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/sale-reporting <https://github.com/OCA/sale-reporting/tree/15.0/sale_section_and_note_revamp>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions sale_section_and_note_revamp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
12 changes: 12 additions & 0 deletions sale_section_and_note_revamp/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "Sale Section and Note Revamp",
"version": "15.0.1.0.0",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Sales",
"depends": ["sale"],
"website": "https://github.com/OCA/sale-reporting",
"installable": True,
"auto_install": False,
"application": False,
}
3 changes: 3 additions & 0 deletions sale_section_and_note_revamp/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import display_line_mixin
from . import sale_order_line
from . import sale_order
177 changes: 177 additions & 0 deletions sale_section_and_note_revamp/models/display_line_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields, models


class DisplayLineMixin(models.AbstractModel):
_name = "display.line.mixin"
_description = """This model intends to ease the propagation and generation
of sections and notes to any model in relation with a sale order line"""
_order = "sequence ASC, id ASC"

sequence = fields.Integer()
previous_line_id = fields.Many2one("sale.order.line")
next_line_id = fields.Many2one("sale.order.line")
display_type = fields.Char(store=False)

def get_section_or_note_class(self):
"""Return the class names depending of the display type

Classes come from sale sections scss configuration
"""
if self.is_section():
return "bg-200 font-weight-bold o_line_section"
elif self.is_note():
return "font-italic o_line_note"
return ""

def is_section(self):
"""Quick method helper"""
self.ensure_one()
return self.display_type == "line_section"

def is_note(self):
"""Quick method helper"""
self.ensure_one()
return self.display_type == "line_note"

def is_section_or_note(self):
"""Quick method helper"""
self.ensure_one()
return self.is_section() or self.is_note()

def has_section(self):
"""Quick method helper"""
return bool(self.get_section())

def has_note(self):
"""Quick method helper"""
return bool(self.get_note())

def get_section(self):
"""Returns the section of the current line"""
if not self.is_section():
previous_record = self.previous_line_id
while previous_record:
if previous_record.is_section():
return previous_record
previous_record = previous_record.previous_line_id

def get_note(self):
"""Return the note positioned after an order line even if a section"""
if not self.is_note():
if self.next_line_id.is_note():
return self.next_line_id

def get_section_subtotal(self, fields=None):
"""Return the sum of each individual provided fields
:param fields: a list of fields to sum
:returns: a dict as {field: sum values}
"""
self.ensure_one()
if self.is_section():
if not fields:
fields = []
return {
field: sum(self._get_section_lines(with_notes=False).mapped(field))
for field in fields
}

def _get_section_lines(self, with_notes=True):
"""Get all order lines in a section.
:param with_notes: Include note lines
"""
self.ensure_one()
if self.is_section():
result = self.env[self._name]
next_record = self.next_line_id
while next_record:
if next_record.is_section():
break
result |= next_record
next_record = next_record.next_line_id
notes = result.filtered(lambda r: r.is_note())
return result if with_notes else result - notes

def add_line(self, line, before=False):
"""Add a line after or before the current line"""
self.ensure_one()
# Tie together previous line and next line of the line given in argument
if line.previous_line_id:
line.previous_line_id.next_line_id = line.next_line_id
if line.next_line_id:
line.next_line_id.previous_line_id = line.previous_line_id

# Insersion of the line given inbetween current line and previous/next line
if before:
if self.previous_line_id:
self.previous_line_id.next_line_id = line
line.previous_line_id = self.previous_line_id
self.previous_line_id = line
line.next_line_id = self
else:
if self.next_line_id:
self.next_line_id.previous_line_id = line
line.next_line_id = self.next_line_id
self.next_line_id = line
line.previous_line_id = self

def prepare_section_or_note_values(self, order_line):
"""This method is intended to be used to `convert` a display line to
the current model

It is mainly used for display lines injection to delivery reports

:param order_line: a sale.order.line record
"""
self.ensure_one()
raise NotImplementedError

def inject_sections_and_notes(self):
"""This method inject all related display lines to the right position
for the inheriting model

See sale.order::calc_order_lines_dependencies for further explanations
"""
model_name = self._name

def add_section_or_note(record, display_line):
values = record.prepare_section_or_note_values(display_line)
return self.new(values)

result = self.env[model_name]
# Avoid repeating display lines over hierarchy
done_record = self.env["sale.order.line"]
total_records = len(self)
for index, record in enumerate(self.sorted("sequence")):

# We parse all previous lines to retrieve every section or notes
previous_lines = self.env[model_name]
previous_record = record.previous_line_id
while previous_record:
if previous_record in done_record:
break
elif previous_record.is_section_or_note():
previous_lines |= add_section_or_note(record, previous_record)
done_record |= previous_record
previous_record = previous_record.previous_line_id
# As we parsed backwards, we need to sort back the lines ^^
if previous_lines:
result |= previous_lines.sorted("sequence")

# Then of course we add our current line
result |= record

# Manage last lines if sections or notes
if (index + 1) >= total_records and record.next_line_id:
next_record = record.next_line_id
while next_record:
if next_record in done_record:
break
elif next_record.is_section_or_note():
result |= add_section_or_note(record, next_record)
done_record |= next_record
next_record = next_record.next_line_id

return result
19 changes: 19 additions & 0 deletions sale_section_and_note_revamp/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import models


class SaleOrder(models.Model):
_inherit = "sale.order"

def calc_order_lines_dependencies(self):
"""Link order lines with their respective siblings"""
for order in self:
order.order_line.write({"previous_line_id": False, "next_line_id": False})
previous_line = self.env["sale.order.line"]
for order_line in order.order_line.sorted("sequence"):
order_line.write({"previous_line_id": previous_line.id})
if previous_line:
previous_line.write({"next_line_id": order_line.id})
previous_line = order_line
21 changes: 21 additions & 0 deletions sale_section_and_note_revamp/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import api, models


class SaleOrderLine(models.Model):
_name = "sale.order.line"
_inherit = ["sale.order.line", "display.line.mixin"]

@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
res.order_id.calc_order_lines_dependencies()
return res

def write(self, vals):
res = super().write(vals)
if any(fname in vals for fname in ["order_id", "sequence"]):
self.order_id.calc_order_lines_dependencies()
return res
1 change: 1 addition & 0 deletions sale_section_and_note_revamp/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Stéphane Mangin <stephane.mangin@camptocamp.com>
2 changes: 2 additions & 0 deletions sale_section_and_note_revamp/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This module provides high level method helpers to ease sale sections and notes
management and use elsewhere.
Loading
Loading