Skip to content

Commit

Permalink
[15.0][ADD] sale_section_and_note_revamp
Browse files Browse the repository at this point in the history
  • Loading branch information
StephaneMangin committed Nov 23, 2023
1 parent 01ec1c9 commit 843cdcf
Show file tree
Hide file tree
Showing 14 changed files with 494 additions and 0 deletions.
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-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |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 <[email protected]>

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
155 changes: 155 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,155 @@
# 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().mapped(field))
for field in fields
}

def _get_section_lines(self, without_notes=True):
"""Get all order lines in a section.
:param without_notes: Do not 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
if without_notes and next_record.is_note():
continue
result |= next_record
next_record = next_record.next_line_id
return result

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::compute_order_lines_dependency 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
31 changes: 31 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,31 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import api, models


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

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

def write(self, vals):
result = super().write(vals)
if "order_line" in vals:
self.calc_order_lines_dependencies()
return result

def calc_order_lines_dependencies(self):
"""Link order lines with their respective siblings"""
self.order_line.write({"previous_line_id": False, "next_line_id": False})
for order in self:
previous_line = False
for order_line in order.order_line.sorted("sequence"):
order_line.previous_line_id = previous_line
if previous_line:
previous_line.next_line_id = order_line
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 <[email protected]>
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.
1 change: 1 addition & 0 deletions sale_section_and_note_revamp/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_display_line_mixin
Loading

0 comments on commit 843cdcf

Please sign in to comment.