Initial commit: OCA Financial packages (186 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:04 +02:00
commit 3e0e8473fb
8757 changed files with 947473 additions and 0 deletions

View file

@ -0,0 +1,11 @@
from . import account_ecotax_category
from . import account_ecotax_classification
from . import account_move
from . import account_move_line
from . import ecotax_line_product
from . import product_template
from . import ecotax_line_mixin
from . import account_move_line_ecotax
from . import product_product
from . import ecotax_sector
from . import ecotax_collector

View file

@ -0,0 +1,15 @@
# Copyright 2021 Camptocamp
# @author Silvio Gregorini <silvio.gregorini@camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class AccountEcotaxCategory(models.Model):
_name = "account.ecotax.category"
_description = "Account Ecotax Category"
name = fields.Char(required=True)
code = fields.Char(required=True)
description = fields.Char()
active = fields.Boolean(default=True)

View file

@ -0,0 +1,92 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class AccountEcotaxClassification(models.Model):
_name = "account.ecotax.classification"
_description = "Account Ecotax Classification"
name = fields.Char(required=True)
code = fields.Char()
ecotax_type = fields.Selection(
[("fixed", "Fixed"), ("weight_based", "Weight based")],
required=True,
help="If ecotax is weight based,"
"the ecotax coef must take into account\n"
"the weight unit of measure (kg by default)",
)
ecotax_coef = fields.Float(
digits="Ecotax", compute="_compute_ecotax_vals", readonly=False, store=True
)
default_fixed_ecotax = fields.Float(
digits="Ecotax",
help="Default fixed ecotax amount.",
compute="_compute_ecotax_vals",
readonly=False,
store=True,
)
categ_id = fields.Many2one(
comodel_name="account.ecotax.category",
string="Category",
)
sector_id = fields.Many2one(
comodel_name="ecotax.sector",
string="Ecotax sector",
)
collector_id = fields.Many2one(
comodel_name="ecotax.collector",
string="Ecotax collector",
)
active = fields.Boolean(default=True)
company_id = fields.Many2one(
comodel_name="res.company",
default=lambda self: self.env.company,
help="Specify a company"
" if you want to define this Ecotax Classification only for specific"
" company. Otherwise, this Fiscal Classification will be available"
" for all companies.",
)
product_status = fields.Selection(
[("M", "Menager"), ("P", "Professionnel")],
required=True,
)
supplier_status = fields.Selection(
[
("MAN", "Manufacturer"),
("RES", "Reseller, under their own brand"),
("INT", "Introducer"),
("IMP", "Importer"),
("REM", "Remote vendor"),
],
required=True,
help="MAN ==> Manufacturer: is locally established in the country, "
"and manufactures goods which are subject to ecotaxes\n"
"under their own name and brand, or designs such goods, "
"subcontracts the manufacturing and then sells them under "
"their own name and brand\n"
"RES ==> Reseller, under their own brand: is locally established "
"in the country, and sells under their own name or brand goods"
" subject to ecotax manufactured by others\n"
"INT ==> Introducer: is locally established and sells on the local "
"market goods subject to ecotax coming from other countries "
"of the European Union\n"
"IMP ==> Importer: is established in France, and sells on the local"
" market goods subject to ecotax coming from countries outside"
"the European Union\n"
"REM ==> Remote vendor: is established in another country of "
"the European Union or outside the EU, and remotely sells good "
"subject to ecotaxes to customers in the country",
)
intrastat_code = fields.Char()
scale_code = fields.Char()
@api.depends("ecotax_type")
def _compute_ecotax_vals(self):
for classif in self:
if classif.ecotax_type == "weight_based":
classif.default_fixed_ecotax = 0
elif classif.ecotax_type == "fixed":
classif.ecotax_coef = 0

View file

@ -0,0 +1,65 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.tools.misc import formatLang
class AccountMove(models.Model):
_inherit = "account.move"
amount_ecotax = fields.Float(
digits="Ecotax",
string="Included Ecotax",
store=True,
compute="_compute_ecotax",
)
@api.depends("invoice_line_ids.subtotal_ecotax")
def _compute_ecotax(self):
for move in self:
move.amount_ecotax = sum(move.line_ids.mapped("subtotal_ecotax"))
@api.model
def _get_tax_totals(
self, partner, tax_lines_data, amount_total, amount_untaxed, currency
):
"""Include Ecotax when this method is called upon a single invoice
NB: `_get_tax_totals()` is called when field `tax_totals_json` is
computed, which is used in invoice form view to display taxes and
totals.
"""
res = super()._get_tax_totals(
partner, tax_lines_data, amount_total, amount_untaxed, currency
)
if len(self) != 1:
return res
base_amt = self.amount_total
ecotax_amt = self.amount_ecotax
if not ecotax_amt:
return res
env = self.with_context(lang=partner.lang).env
fmt_ecotax_amt = formatLang(env, ecotax_amt, currency_obj=currency)
fmt_base_amt = formatLang(env, base_amt, currency_obj=currency)
data = list(res["groups_by_subtotal"].get(_("Untaxed Amount")) or [])
data.append(
{
"tax_group_name": _("Included Ecotax"),
"tax_group_amount": ecotax_amt,
"formatted_tax_group_amount": fmt_ecotax_amt,
"tax_group_base_amount": base_amt,
"formatted_tax_group_base_amount": fmt_base_amt,
"tax_group_id": False, # Not an actual tax
"group_key": "Included Ecotax",
}
)
res["groups_by_subtotal"][_("Untaxed Amount")] = data
return res
def _get_formatted_ecotax_amount(self):
self.ensure_one()
return formatLang(self.env, self.amount_ecotax, currency_obj=self.currency_id)

View file

@ -0,0 +1,84 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import Command, api, fields, models
class AcountMoveLine(models.Model):
_inherit = "account.move.line"
ecotax_line_ids = fields.One2many(
"account.move.line.ecotax",
"account_move_line_id",
compute="_compute_ecotax_line_ids",
store=True,
readonly=False,
string="Ecotax lines",
copy=True,
)
subtotal_ecotax = fields.Float(
string="Ecotax", digits="Ecotax", store=True, compute="_compute_ecotax"
)
ecotax_amount_unit = fields.Float(
digits="Ecotax",
string="Ecotax Unit",
store=True,
compute="_compute_ecotax",
)
def _get_ecotax_amounts(self):
self.ensure_one()
amount_unit = sum(self.ecotax_line_ids.mapped("amount_unit"))
subtotal_ecotax = sum(self.ecotax_line_ids.mapped("amount_total"))
return amount_unit, subtotal_ecotax
@api.depends(
"currency_id",
"ecotax_line_ids.amount_unit",
"ecotax_line_ids.amount_total",
)
def _compute_ecotax(self):
for line in self:
amount_unit, amount_total = line._get_ecotax_amounts()
line.ecotax_amount_unit = amount_unit
line.subtotal_ecotax = amount_total
def _get_new_vals_list(self):
self.ensure_one()
new_vals_list = [
Command.create(
{
"classification_id": ecotaxline_prod.classification_id.id,
"force_amount_unit": ecotaxline_prod.force_amount,
}
)
for ecotaxline_prod in self.product_id.all_ecotax_line_product_ids
]
return new_vals_list
@api.depends("product_id")
def _compute_ecotax_line_ids(self):
"""Unlink and recreate ecotax_lines when modifying the product_id."""
for line in self:
if line.move_id.move_type not in ("out_invoice", "out_refund"):
continue
delete_vals_list = [
Command.delete(taxline.id) for taxline in line.ecotax_line_ids
]
new_vals_list = line._get_new_vals_list()
update = new_vals_list + delete_vals_list
line.ecotax_line_ids = update
def edit_ecotax_lines(self):
view = {
"name": ("Ecotax classification"),
"view_type": "form",
"view_mode": "form",
"res_model": "account.move.line",
"view_id": self.env.ref("account_ecotax.view_move_line_ecotax_form").id,
"type": "ir.actions.act_window",
"target": "new",
"res_id": self.id,
}
return view

View file

@ -0,0 +1,33 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class AccountMoveLineEcotax(models.Model):
_name = "account.move.line.ecotax"
_inherit = "ecotax.line.mixin"
_description = "Account move line ecotax"
account_move_line_id = fields.Many2one(
comodel_name="account.move.line",
string="Account move line",
required=True,
readonly=True,
index=True,
auto_join=True,
ondelete="cascade",
)
product_id = fields.Many2one(
"product.product",
related="account_move_line_id.product_id",
readonly=True,
store=True,
)
quantity = fields.Float(
related="account_move_line_id.quantity", readonly=True, store=True
)
currency_id = fields.Many2one(
related="account_move_line_id.currency_id", readonly=True, store=True
)

View file

@ -0,0 +1,14 @@
# Copyright 2021 Camptocamp
# @author Silvio Gregorini <silvio.gregorini@camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class EcotaxCollector(models.Model):
_name = "ecotax.collector"
_description = "Ecotax collector"
name = fields.Char(required=True)
partner_id = fields.Many2one("res.partner", string="Partner", required=False)
active = fields.Boolean(default=True)

View file

@ -0,0 +1,66 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class EcotaxLineMixin(models.AbstractModel):
"""Mixin class for objects which can be used to save
multi ecotax classification by account move line
or sale order line."""
_name = "ecotax.line.mixin"
_description = "Ecotax Line Mixin"
product_id = fields.Many2one("product.product", string="Product", readonly=True)
currency_id = fields.Many2one("res.currency", string="Currency")
classification_id = fields.Many2one(
"account.ecotax.classification",
string="Classification",
ondelete="restrict",
)
amount_unit = fields.Float(
digits="Ecotax",
compute="_compute_ecotax",
help="Ecotax Amount computed from Classification or Manual ecotax",
store=True,
)
force_amount_unit = fields.Float(
digits="Ecotax",
help="Force ecotax.\n"
"Allow to add a subtitle to the default Ecotax Classification",
)
amount_total = fields.Float(
digits="Ecotax",
compute="_compute_ecotax",
help="Ecotax Amount total computed from Classification or forced ecotax amount",
store=True,
)
quantity = fields.Float(digits="Product Unit of Measure", readonly=True)
@api.depends(
"classification_id",
"force_amount_unit",
"product_id",
"quantity",
)
def _compute_ecotax(self):
for ecotaxline in self:
ecotax_classif = ecotaxline.classification_id
if ecotaxline.force_amount_unit:
# force ecotax amount
amount = ecotaxline.force_amount_unit
elif ecotax_classif.ecotax_type == "weight_based":
amount = ecotax_classif.ecotax_coef * (
ecotaxline.product_id.weight or 0.0
)
else:
amount = ecotax_classif.default_fixed_ecotax
ecotaxline.amount_unit = amount
total = ecotaxline.amount_unit * ecotaxline.quantity
if ecotaxline.currency_id:
total = ecotaxline.currency_id.round(total)
ecotaxline.amount_total = total

View file

@ -0,0 +1,83 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class EcotaxLineProduct(models.Model):
"""class for objects which can be used to save mutiple ecotax classifications
by product."""
_name = "ecotax.line.product"
_description = "Ecotax Line product"
product_tmpl_id = fields.Many2one(
"product.template", string="Product Template", readonly=True
)
product_id = fields.Many2one("product.product", string="Product", readonly=True)
currency_id = fields.Many2one(related="product_tmpl_id.currency_id", readonly=True)
classification_id = fields.Many2one(
"account.ecotax.classification",
string="Classification",
)
force_amount = fields.Float(
digits="Ecotax",
help="Force ecotax amount.\n"
"Allow to substitute default Ecotax Classification",
)
amount = fields.Float(
digits="Ecotax",
compute="_compute_ecotax",
help="Ecotax Amount computed form Classification or forced ecotax amount",
store=True,
)
display_name = fields.Char(compute="_compute_display_name")
@api.depends("classification_id", "amount")
def _compute_display_name(self):
for rec in self:
rec.display_name = "%s (%s)" % (
rec.classification_id.name,
rec.amount,
)
@api.depends(
"classification_id",
"classification_id.ecotax_type",
"classification_id.ecotax_coef",
"product_tmpl_id",
"product_tmpl_id.weight",
"product_id",
"product_id.weight",
"force_amount",
)
def _compute_ecotax(self):
for ecotaxline in self:
ecotax_cls = ecotaxline.classification_id
if ecotax_cls.ecotax_type == "weight_based":
amount = ecotax_cls.ecotax_coef * (
ecotaxline.product_tmpl_id.weight
or ecotaxline.product_id.weight
or 0.0
)
else:
amount = ecotax_cls.default_fixed_ecotax
# force ecotax amount
if ecotaxline.force_amount:
amount = ecotaxline.force_amount
ecotaxline.amount = amount
_sql_constraints = [
(
"unique_classification_id_by_product",
"UNIQUE(classification_id, product_id)",
"Only one ecotax classification occurrence by product",
),
(
"unique_classification_id_by_product_tmpl",
"UNIQUE(classification_id, product_tmpl_id)",
"Only one ecotax classification occurrence by product Template",
),
]

View file

@ -0,0 +1,14 @@
# Copyright 2021 Camptocamp
# @author Silvio Gregorini <silvio.gregorini@camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class EcotaxSector(models.Model):
_name = "ecotax.sector"
_description = "Ecotax Sector"
name = fields.Char(required=True)
description = fields.Char()
active = fields.Boolean(default=True)

View file

@ -0,0 +1,90 @@
# Copyright 2021 Camptocamp
# @author Silvio Gregorini <silvio.gregorini@camptocamp.com>
# Copyright 2023 Akretion (http://www.akretion.com)
# # @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
from odoo.osv import expression
class ProductProduct(models.Model):
_inherit = "product.product"
additional_ecotax_line_product_ids = fields.One2many(
"ecotax.line.product",
"product_id",
string="Additional ecotax lines",
copy=True,
domain="[('id', 'not in', ecotax_line_product_ids)]",
)
all_ecotax_line_product_ids = fields.One2many(
"ecotax.line.product",
compute="_compute_all_ecotax_line_product_ids",
search="_search_all_ecotax_line_product_ids",
string="All ecotax lines",
help="Contain all ecotaxs classification defined in product template"
"and the additionnal.\n"
"ecotaxs defined in product variant. For more details"
"see the product variant accounting tab",
)
ecotax_amount = fields.Float(
digits="Ecotax",
compute="_compute_product_ecotax",
store=True,
help="Ecotax Amount computed form all ecotax line classification",
)
fixed_ecotax = fields.Float(
compute="_compute_product_ecotax",
store=True,
help="Fixed ecotax of the Ecotax Classification",
)
weight_based_ecotax = fields.Float(
compute="_compute_product_ecotax",
store=True,
help="Ecotax value :\n" "product weight * ecotax coef of Ecotax Classification",
)
@api.depends("ecotax_line_product_ids", "additional_ecotax_line_product_ids")
def _compute_all_ecotax_line_product_ids(self):
for product in self:
product.all_ecotax_line_product_ids = (
product.ecotax_line_product_ids
| product.additional_ecotax_line_product_ids
)
def _search_all_ecotax_line_product_ids(self, operator, operand):
if operator in expression.NEGATIVE_TERM_OPERATORS:
return [
("ecotax_line_product_ids", operator, operand),
("additional_ecotax_line_product_ids", operator, operand),
]
return [
"|",
("ecotax_line_product_ids", operator, operand),
("additional_ecotax_line_product_ids", operator, operand),
]
@api.depends(
"all_ecotax_line_product_ids",
"all_ecotax_line_product_ids.classification_id",
"all_ecotax_line_product_ids.classification_id.ecotax_type",
"all_ecotax_line_product_ids.classification_id.ecotax_coef",
"all_ecotax_line_product_ids.force_amount",
"weight",
)
def _compute_product_ecotax(self):
for product in self:
amount_ecotax = 0.0
weight_based_ecotax = 0.0
fixed_ecotax = 0.0
for ecotaxline_prod in product.all_ecotax_line_product_ids:
ecotax_cls = ecotaxline_prod.classification_id
if ecotax_cls.ecotax_type == "weight_based":
weight_based_ecotax += ecotaxline_prod.amount
else:
fixed_ecotax += ecotaxline_prod.amount
amount_ecotax += ecotaxline_prod.amount
product.fixed_ecotax = fixed_ecotax
product.weight_based_ecotax = weight_based_ecotax
product.ecotax_amount = amount_ecotax

View file

@ -0,0 +1,56 @@
# © 2014-2023 Akretion (http://www.akretion.com)
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class ProductTemplate(models.Model):
_inherit = "product.template"
ecotax_line_product_ids = fields.One2many(
"ecotax.line.product",
"product_tmpl_id",
string="Ecotax lines",
copy=True,
)
ecotax_amount = fields.Float(
digits="Ecotax",
compute="_compute_ecotax",
help="Ecotax Amount computed from Classification",
store=True,
)
fixed_ecotax = fields.Float(
compute="_compute_ecotax",
help="Fixed ecotax of the Ecotax Classification",
store=True,
)
weight_based_ecotax = fields.Float(
compute="_compute_ecotax",
help="Ecotax value :\nproduct weight * ecotax coef of Ecotax Classification",
store=True,
)
@api.depends(
"ecotax_line_product_ids",
"ecotax_line_product_ids.classification_id",
"ecotax_line_product_ids.classification_id.ecotax_type",
"ecotax_line_product_ids.classification_id.ecotax_coef",
"ecotax_line_product_ids.force_amount",
"weight",
)
def _compute_ecotax(self):
for tmpl in self:
amount_ecotax = 0.0
weight_based_ecotax = 0.0
fixed_ecotax = 0.0
for ecotaxline_prod in tmpl.ecotax_line_product_ids:
ecotax_cls = ecotaxline_prod.classification_id
if ecotax_cls.ecotax_type == "weight_based":
weight_based_ecotax += ecotaxline_prod.amount
else:
fixed_ecotax += ecotaxline_prod.amount
amount_ecotax += ecotaxline_prod.amount
tmpl.fixed_ecotax = fixed_ecotax
tmpl.weight_based_ecotax = weight_based_ecotax
tmpl.ecotax_amount = amount_ecotax