diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c283fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +/.venv +/.pytest_cache + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +*.eggs + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Pycharm +.idea + +# Eclipse +.settings + +# Visual Studio cache/options directory +.vs/ +.vscode + +# OSX Files +.DS_Store + +# Django stuff: +*.log + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Sphinx documentation +docs/_build/ + +# Backup files +*~ +*.swp + +# OCA rules +!static/lib/ diff --git a/__manifest__.py b/__manifest__.py index 52d08de..9d4ee19 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -27,7 +27,10 @@ 'author': 'Bemade Inc.', 'website': 'https://www.bemade.org', 'license': 'OPL-1', - 'depends': ['sale', 'account'], + 'depends': ['sale', + 'account', + 'bemade_partner_root_ancestor', + ], 'data': [], 'demo': [], 'installable': True, diff --git a/models/__init__.py b/models/__init__.py index 8b13789..fdc1257 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,3 @@ - +from . import res_partner +from . import sale_order +from . import account_move diff --git a/models/account_move.py b/models/account_move.py index e69de29..e5b191a 100644 --- a/models/account_move.py +++ b/models/account_move.py @@ -0,0 +1,32 @@ +from odoo import models, fields, api + + +class AccountMove(models.Model): + _inherit = 'account.move' + + billing_contacts = fields.Many2many(comodel_name='res.partner', + string="Billing Contacts", + compute='_compute_billing_contacts', + inverse='_inverse_billing_contacts', + store=True) + + @api.depends('line_ids.sale_line_ids.order_id', 'partner_id') + def _compute_billing_contacts(self): + for rec in self: + order_id = rec.line_ids and rec.line_ids.mapped('sale_line_ids').mapped('order_id') + if order_id and len(order_id) == 1 and order_id.billing_contacts: + rec.billing_contacts = order_id.billing_contacts + else: + rec.billing_contacts = rec.partner_id.billing_contacts + + def _inverse_billing_contacts(self): + pass + + def _post(self, soft=True): + # Override the original method to subscribe the partner's billing contacts instead of self.partner_id + initial_subscribers = self.message_partner_ids.ids + final_subscribers = initial_subscribers + self.partner_id.billing_contacts.ids + posted = super()._post() + self.message_unsubscribe(self.message_partner_ids - initial_subscribers) + self.message_subscribe(final_subscribers) + return posted diff --git a/models/res_partner.py b/models/res_partner.py index fa220a5..ae988ff 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -4,13 +4,17 @@ from odoo import models, fields, api, _, Command class Partner(models.Model): _inherit = 'res.partner' - def _billing_contacts_domain(self): - self.ensure_one() - return [('is_company', '=', False), ('root_ancestor', '=', self.root_ancestor)] billing_contacts = fields.Many2many(string='Default Billing Contacts', comodel_name='res.partner', - relation='res_partner_billing_contact_rel', - column1='billing_contact_id', - column2='billed_partner_id', - domain=_billing_contacts_domain, - tracking=True) \ No newline at end of file + compute='_compute_billing_contacts', + inverse='_inverse_billing_contacts') + + @api.depends('child_ids.type') + def _compute_billing_contacts(self): + for rec in self: + rec.billing_contacts = rec.child_ids.filtered(lambda r: r.type == 'invoice') + + @api.depends('billing_contacts') + def _inverse_billing_contacts(self): + for partner in self.mapped('billing_contacts'): + partner.type = 'invoice' diff --git a/models/sale_order.py b/models/sale_order.py index ed1dad9..877cc5d 100644 --- a/models/sale_order.py +++ b/models/sale_order.py @@ -4,3 +4,16 @@ from odoo import models, fields, api, _, Command class SaleOrder(models.Model): _inherit = 'sale.order' + billing_contacts = fields.Many2many(comodel_name='res.partner', + string='Billing Contacts', + compute='_compute_billing_contacts', + inverse='_inverse_billing_contacts', + store=True) + + @api.depends('partner_id') + def _compute_billing_contacts(self): + for rec in self: + rec.billing_contacts = rec.partner_id.billing_contacts + + def _inverse_billing_contacts(self): + pass diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..219fd8e --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +from . import test_billing_contacts diff --git a/tests/test_billing_contacts.py b/tests/test_billing_contacts.py new file mode 100644 index 0000000..47f0a9b --- /dev/null +++ b/tests/test_billing_contacts.py @@ -0,0 +1,40 @@ +from odoo.tests import TransactionCase, tagged + + +class TestBillingContacts(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + partner = cls.env['res.partner'].create + cls.parent_co = partner({ + 'name': 'Partner', + 'company_type': 'company', + }) + cls.billing_contact1 = partner({ + 'name': 'Billing Contact 1', + 'company_type': 'person', + 'email': 'billingcontact1@partner.co', + 'parent_id': cls.parent_co.id, + 'type': 'invoice', + }) + cls.billing_contact2 = partner({ + 'name': 'Billing Contact 2', + 'company_type': 'person', + 'email': 'billingcontact2@partner.co', + 'parent_id': cls.parent_co.id, + 'type': 'invoice', + }) + cls.non_billing_contact = partner({ + 'name': 'Non-billing contact', + 'company_type': 'person', + 'email': 'not_billing@partner.co', + 'parent_id': cls.parent_co.id, + 'type': 'contact', + }) + + @tagged('-at_install', 'post_install') + def test_sale_order_defaults(self): + billing_contacts = self.parent_co.billing_contacts + self.assertTrue(self.billing_contact1 in self.parent_co.billing_contacts) + self.assertTrue(self.billing_contact2 in self.parent_co.billing_contacts) + self.assertTrue(self.non_billing_contact not in self.parent_co.billing_contacts)