chore: import enterprise addons 19.0 bulk snapshot

This commit is contained in:
1b-agent committed 2026-09-16 10:13:08 +00:00
commit ca4af030d3
39951 files changed
+15570005

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import account_move_line
from . import account_move
from . import product_template
from . import sale_order_line
from . import stock_landed_cost
from . import stock_lot
from . import stock_move_line
from . import stock_quant
@@ -0,0 +1,50 @@
# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, _
from odoo.tools.misc import format_date
class AccountMove(models.Model):
_inherit = 'account.move'
def _l10n_mx_edi_add_invoice_cfdi_values(self, cfdi_values):
# OVERRIDE
res = super()._l10n_mx_edi_add_invoice_cfdi_values(cfdi_values)
if cfdi_values.get("errors"):
return res
customs_dates = self._l10n_mx_edi_get_formatted_date_per_customs_number()
for base_line in cfdi_values['base_lines']:
record = base_line['record']
if not record.l10n_mx_edi_can_use_customs_invoicing:
continue
base_line_cfdi_values = base_line['l10n_mx_cfdi_values']
customs_numbers = record._l10n_mx_edi_get_custom_numbers()
formatted_dates = ",".join(
customs_date for customs in customs_numbers
if (customs_date := customs_dates.get(customs))
)
if formatted_dates:
base_line_cfdi_values['description'] += _("\nCustoms Number Date: %s", formatted_dates)
return res
def _l10n_mx_edi_get_formatted_date_per_customs_number(self):
self.ensure_one()
landed_costs = self.env["stock.landed.cost"].sudo().search_fetch(
[
("l10n_mx_edi_customs_number", "in",
self.invoice_line_ids.mapped("l10n_mx_edi_customs_number")),
("state", "=", "done"),
],
field_names=["date"]
)
customs_dates = {}
for lc in landed_costs:
customs_dates[lc.l10n_mx_edi_customs_number] = (
format_date(self.env, lc.date, date_format='yyyy-MM-dd')
if lc.date else ''
)
return customs_dates
@@ -0,0 +1,12 @@
from odoo import api, fields, models
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
l10n_mx_edi_can_use_customs_invoicing = fields.Boolean(compute="_compute_l10n_mx_edi_can_use_customs_invoicing")
@api.depends('sale_line_ids')
def _compute_l10n_mx_edi_can_use_customs_invoicing(self):
for aml in self:
aml.l10n_mx_edi_can_use_customs_invoicing = aml.sale_line_ids[:1].l10n_mx_edi_can_use_customs_invoicing
@@ -0,0 +1,43 @@
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class ProductTemplate(models.Model):
_inherit = 'product.template'
l10n_mx_edi_use_customs_invoicing = fields.Boolean(
company_dependent=True,
help="""If enabled, customs numbers will be set automatically when creating an invoice line from a sale line.
An invoice line will be created for each customs. Toggling this option will affect future created sale lines.
""",
tracking=True,
)
l10n_mx_edi_can_use_customs_invoicing = fields.Boolean(
string='Customs invoicing',
compute="_compute_l10n_mx_edi_can_use_customs_invoicing",
inverse="_inverse_l10n_mx_edi_can_use_customs_invoicing",
)
@api.depends('invoice_policy', 'tracking', 'lot_valuated', 'company_id')
@api.depends_context('company')
def _compute_l10n_mx_edi_can_use_customs_invoicing(self):
for product in self:
product.l10n_mx_edi_can_use_customs_invoicing = (
product.invoice_policy == 'delivery'
and product.tracking != 'none'
and product.lot_valuated
and product.l10n_mx_edi_use_customs_invoicing
)
def _inverse_l10n_mx_edi_can_use_customs_invoicing(self):
for product in self:
product.l10n_mx_edi_use_customs_invoicing = product.l10n_mx_edi_can_use_customs_invoicing
@api.constrains('l10n_mx_edi_use_customs_invoicing', 'l10n_mx_edi_can_use_customs_invoicing')
def _check_l10n_mx_edi_use_customs_invoicing(self):
for product in self:
if product.l10n_mx_edi_use_customs_invoicing and not product.l10n_mx_edi_can_use_customs_invoicing:
raise ValidationError(_(
"Automatic computation of customs numbers is only allowed for products tracked\n"
"by lot/sn with valuation and invoiced by qty delivered."
))
@@ -0,0 +1,80 @@
from collections import defaultdict
from odoo import api, fields, models
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
l10n_mx_edi_can_use_customs_invoicing = fields.Boolean(
compute='_compute_l10n_mx_edi_can_use_customs_invoicing',
precompute=True,
store=True
)
@api.depends('product_id')
def _compute_l10n_mx_edi_can_use_customs_invoicing(self):
for so_line in self:
company = so_line.order_id.company_id or self.env.company
so_line.l10n_mx_edi_can_use_customs_invoicing = so_line.product_id.with_company(company).l10n_mx_edi_can_use_customs_invoicing
def _prepare_invoice_lines_vals_list(self, **optional_values):
# OVERRIDE
vals_list = super()._prepare_invoice_lines_vals_list(**optional_values)
if self.company_id.country_code == 'MX' and self.l10n_mx_edi_can_use_customs_invoicing:
new_vals_list = []
qties_to_invoice = self._get_l10n_mx_edi_customs_qty_to_invoice()
sign = 1 if self.qty_to_invoice < 0 else -1
# Considering that there might be many invoice vals now for this sole line
for vals in vals_list:
if vals["display_type"] != 'product' or vals["is_downpayment"]:
continue
invoiced_qty = vals["quantity"]
for customs, qty in qties_to_invoice.items():
if self.product_uom_id.is_zero(invoiced_qty):
break
if self.product_uom_id.is_zero(qty) or not self.product_uom_id.compare(qty, 0) * sign <= 0:
continue
qty_to_invoice = min(invoiced_qty, qty, key=abs)
new_vals_list.append({
**vals,
"l10n_mx_edi_customs_number": customs,
"quantity": qty_to_invoice,
})
invoiced_qty -= qty_to_invoice
qties_to_invoice[customs] -= qty_to_invoice
vals_list = new_vals_list
return vals_list
def _get_l10n_mx_edi_customs_qty_to_invoice(self):
self.ensure_one()
def _compute_qty(qty, from_unit, inverse):
return from_unit._compute_quantity(
qty * (-1 if inverse else 1), self.product_uom_id, rounding_method="HALF-UP"
)
qty_to_invoice_by_customs = defaultdict(float)
outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves()
sml_by_customs = (outgoing_moves | incoming_moves).move_line_ids.filtered(
lambda ml: ml.state == 'done' and ml.product_id.tracking != 'none'
).grouped(lambda ml: ml.lot_id.sudo().l10n_mx_edi_customs_number)
for customs, move_lines in sml_by_customs.items():
delivered_qty = sum(
_compute_qty(ml.quantity, ml.product_uom_id, ml.location_usage == 'customer') for ml in move_lines
)
qty_to_invoice_by_customs[customs] = delivered_qty
invl_by_customs = self._get_invoice_lines().filtered(
lambda l: l.parent_state != 'cancel' or l.move_id.payment_state == 'invoicing_legacy'
).grouped("l10n_mx_edi_customs_number")
for customs, lines in invl_by_customs.items():
invoiced_qty = sum(
_compute_qty(invl.quantity, invl.product_uom_id, invl.move_type == 'out_refund') for invl in lines
)
qty_to_invoice_by_customs[customs] -= invoiced_qty
return qty_to_invoice_by_customs
@@ -0,0 +1,61 @@
# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from odoo.addons.l10n_mx_edi_extended.models.account_move import CUSTOM_NUMBERS_PATTERN
class StockLandedCost(models.Model):
_inherit = 'stock.landed.cost'
l10n_mx_edi_customs_number = fields.Char(
help='Optional field for entering the customs information in the case '
'of first-hand sales of imported goods or in the case of foreign trade'
' operations with goods or services.\n'
'The format must be:\n'
' - 2 digits of the year of validation followed by two spaces.\n'
' - 2 digits of customs clearance followed by two spaces.\n'
' - 4 digits of the serial number followed by two spaces.\n'
' - 1 digit corresponding to the last digit of the current year, '
'except in case of a consolidated customs initiated in the previous '
'year of the original request for a rectification.\n'
' - 6 digits of the progressive numbering of the custom.',
string='Customs number', size=21, copy=False)
_l10n_mx_edi_customs_number = models.Constraint(
'UNIQUE (l10n_mx_edi_customs_number)',
"The custom number must be unique!",
)
fiscal_country_codes = fields.Char(related="company_id.country_code")
@api.constrains('l10n_mx_edi_customs_number')
def _check_l10n_mx_edi_customs_number(self):
help_message = self._fields['l10n_mx_edi_customs_number'].help.split('\n', 1)[1]
for landed_cost in self:
if not landed_cost.l10n_mx_edi_customs_number:
continue
custom_number = landed_cost.l10n_mx_edi_customs_number.strip()
if not CUSTOM_NUMBERS_PATTERN.match(custom_number):
raise ValidationError(self.env._(
"Error!, The format of the customs number is incorrect. \n%s\n"
"For example: 15 48 3009 0001234", help_message))
def button_validate(self):
res = super().button_validate()
for cost in self:
if cost.state != 'done' or cost.company_id.country_id.code != 'MX':
continue
lots = cost.picking_ids.move_line_ids.filtered(
lambda ml: (
ml.product_id.l10n_mx_edi_can_use_customs_invoicing
and ml.state == 'done'
and not ml.lot_id.l10n_mx_edi_landed_cost_id
)
).mapped("lot_id")
if lots:
lots.l10n_mx_edi_landed_cost_id = cost
return res
+27
View File
@@ -0,0 +1,27 @@
from odoo import fields, models
from odoo.addons.l10n_mx_edi_extended.models.account_move import CUSTOM_NUMBERS_PATTERN
class StockLot(models.Model):
_inherit = "stock.lot"
l10n_mx_edi_landed_cost_id = fields.Many2one(
comodel_name="stock.landed.cost",
domain="[('state', '=', 'done')]",
inverse="_inverse_l10n_mx_edi_landed_cost_id"
)
l10n_mx_edi_customs_number = fields.Char(related="l10n_mx_edi_landed_cost_id.l10n_mx_edi_customs_number")
fiscal_country_codes = fields.Char(related="product_id.fiscal_country_codes")
def _inverse_l10n_mx_edi_landed_cost_id(self):
for lot in self:
if not lot.product_id.l10n_mx_edi_can_use_customs_invoicing:
continue
split_name = lot.name.rsplit("/", 1)
# If we already have a customs on the name we take only what is not, else we just take the whole name
prefix_name = split_name[0].strip() if len(split_name) > 1 and CUSTOM_NUMBERS_PATTERN.match(split_name[1].strip()) else lot.name
if prefix_name and lot.l10n_mx_edi_customs_number:
lot.name = f"{prefix_name} / {lot.l10n_mx_edi_customs_number}"
else:
lot.name = prefix_name or lot.l10n_mx_edi_customs_number
@@ -0,0 +1,7 @@
from odoo import fields, models
class StockMoveLine(models.Model):
_inherit = "stock.move.line"
l10n_mx_edi_customs_number = fields.Char(related="lot_id.l10n_mx_edi_customs_number")
@@ -0,0 +1,7 @@
from odoo import fields, models
class StockQuant(models.Model):
_inherit = "stock.quant"
l10n_mx_edi_customs_number = fields.Char(related="lot_id.l10n_mx_edi_customs_number")