diff options
Diffstat (limited to 'addons/l10n_it_stock_ddt/models')
| -rw-r--r-- | addons/l10n_it_stock_ddt/models/__init__.py | 5 | ||||
| -rw-r--r-- | addons/l10n_it_stock_ddt/models/account_invoice.py | 85 | ||||
| -rw-r--r-- | addons/l10n_it_stock_ddt/models/stock_picking.py | 71 |
3 files changed, 161 insertions, 0 deletions
diff --git a/addons/l10n_it_stock_ddt/models/__init__.py b/addons/l10n_it_stock_ddt/models/__init__.py new file mode 100644 index 00000000..08842da1 --- /dev/null +++ b/addons/l10n_it_stock_ddt/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import stock_picking +from . import account_invoice
\ No newline at end of file diff --git a/addons/l10n_it_stock_ddt/models/account_invoice.py b/addons/l10n_it_stock_ddt/models/account_invoice.py new file mode 100644 index 00000000..b7713b27 --- /dev/null +++ b/addons/l10n_it_stock_ddt/models/account_invoice.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, api, fields, _ +from odoo.tools.float_utils import float_compare + + +class AccountMove(models.Model): + _inherit = 'account.move' + + l10n_it_ddt_ids = fields.Many2many('stock.picking', compute="_compute_ddt_ids") + l10n_it_ddt_count = fields.Integer(compute="_compute_ddt_ids") + + def _get_ddt_values(self): + """ + We calculate the link between the invoice lines and the deliveries related to the invoice through the + links with the sale order(s). We assume that the first picking was invoiced first. (FIFO) + :return: a dictionary with as key the picking and value the invoice line numbers (by counting) + """ + self.ensure_one() + # We don't consider returns/credit notes as we suppose they will lead to more deliveries/invoices as well + if self.move_type != "out_invoice" or self.state != 'posted': + return {} + line_count = 0 + invoice_line_pickings = {} + for line in self.invoice_line_ids.filtered(lambda l: not l.display_type): + line_count += 1 + done_moves_related = line.sale_line_ids.mapped('move_ids').filtered(lambda m: m.state == 'done' and m.location_dest_id.usage == 'customer') + if len(done_moves_related) <= 1: + if done_moves_related and line_count not in invoice_line_pickings.get(done_moves_related.picking_id, []): + invoice_line_pickings.setdefault(done_moves_related.picking_id, []).append(line_count) + else: + total_invoices = done_moves_related.mapped('sale_line_id.invoice_lines').filtered( + lambda l: l.move_id.state == 'posted' and l.move_id.move_type == 'out_invoice').sorted(lambda l: l.move_id.invoice_date) + total_invs = [(i.product_uom_id._compute_quantity(i.quantity, i.product_id.uom_id), i) for i in total_invoices] + inv = total_invs.pop(0) + # Match all moves and related invoice lines FIFO looking for when the matched invoice_line matches line + for move in done_moves_related.sorted(lambda m: m.date): + rounding = move.product_uom.rounding + move_qty = move.product_qty + while (float_compare(move_qty, 0, precision_rounding=rounding) > 0): + if float_compare(inv[0], move_qty, precision_rounding=rounding) > 0: + inv = (inv[0] - move_qty, inv[1]) + invoice_line = inv[1] + move_qty = 0 + if float_compare(inv[0], move_qty, precision_rounding=rounding) <= 0: + move_qty -= inv[0] + invoice_line = inv[1] + if total_invs: + inv = total_invs.pop(0) + else: + move_qty = 0 #abort when not enough matched invoices + # If in our FIFO iteration we stumble upon the line we were checking + if invoice_line == line and line_count not in invoice_line_pickings.get(move.picking_id, []): + invoice_line_pickings.setdefault(move.picking_id, []).append(line_count) + return invoice_line_pickings + + @api.depends('invoice_line_ids', 'invoice_line_ids.sale_line_ids') + def _compute_ddt_ids(self): + it_out_invoices = self.filtered(lambda i: i.move_type == 'out_invoice' and i.company_id.country_id.code == 'IT') + for invoice in it_out_invoices: + invoice_line_pickings = invoice._get_ddt_values() + pickings = self.env['stock.picking'] + for picking in invoice_line_pickings: + pickings |= picking + invoice.l10n_it_ddt_ids = pickings + invoice.l10n_it_ddt_count = len(pickings) + for invoice in self - it_out_invoices: + invoice.l10n_it_ddt_ids = self.env['stock.picking'] + invoice.l10n_it_ddt_count = 0 + + def get_linked_ddts(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'view_mode': 'tree,form', + 'name': _("Linked deliveries"), + 'res_model': 'stock.picking', + 'domain': [('id', 'in', self.l10n_it_ddt_ids.ids)], + } + + def _prepare_fatturapa_export_values(self): + template_values = super()._prepare_fatturapa_export_values() + template_values['ddt_dict'] = self._get_ddt_values() + return template_values diff --git a/addons/l10n_it_stock_ddt/models/stock_picking.py b/addons/l10n_it_stock_ddt/models/stock_picking.py new file mode 100644 index 00000000..439d5c9f --- /dev/null +++ b/addons/l10n_it_stock_ddt/models/stock_picking.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, api, _ + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + l10n_it_transport_reason = fields.Selection([('sale', 'Sale'), + ('outsourcing', 'Outsourcing'), + ('evaluation', 'Evaluation'), + ('gift', 'Gift'), + ('transfer', 'Transfer'), + ('substitution', 'Substitution'), + ('attemped_sale', 'Attempted Sale'), + ('loaned_use', 'Loaned for Use'), + ('repair', 'Repair')], default="sale", tracking=True, string='Transport Reason') + l10n_it_transport_method = fields.Selection([('sender', 'Sender'), ('recipient', 'Recipient'), ('courier', 'Courier service')], + default="sender", string='Transport Method') + l10n_it_transport_method_details = fields.Char('Transport Note') + l10n_it_parcels = fields.Integer(string="Parcels", default=1) + l10n_it_country_code = fields.Char(related="company_id.country_id.code") + l10n_it_ddt_number = fields.Char('DDT Number', readonly=True) + + def _action_done(self): + super(StockPicking, self)._action_done() + for picking in self.filtered(lambda p: p.picking_type_id.l10n_it_ddt_sequence_id): + picking.l10n_it_ddt_number = picking.picking_type_id.l10n_it_ddt_sequence_id.next_by_id() + + +class StockPickingType(models.Model): + _inherit = 'stock.picking.type' + + l10n_it_ddt_sequence_id = fields.Many2one('ir.sequence') + + def _get_dtt_ir_seq_vals(self, warehouse_id, sequence_code): + if warehouse_id: + wh = self.env['stock.warehouse'].browse(warehouse_id) + ir_seq_name = wh.name + ' ' + _('Sequence') + ' ' + sequence_code + ir_seq_prefix = wh.code + '/' + sequence_code + '/DDT' + else: + ir_seq_name = _('Sequence') + ' ' + sequence_code + ir_seq_prefix = sequence_code + '/DDT' + return ir_seq_name, ir_seq_prefix + + @api.model + def create(self, vals): + company = self.env['res.company'].browse(vals['company_id']) + if 'l10n_it_ddt_sequence_id' not in vals or not vals['l10n_it_ddt_sequence_id'] and vals['code'] == 'outgoing' \ + and company.country_id.code == 'IT': + ir_seq_name, ir_seq_prefix = self._get_dtt_ir_seq_vals(vals.get('warehouse_id'), vals['sequence_code']) + vals['l10n_it_ddt_sequence_id'] = self.env['ir.sequence'].create({ + 'name': ir_seq_name, + 'prefix': ir_seq_prefix, + 'padding': 5, + 'company_id': vals['company_id'], + 'implementation': 'no_gap', + }).id + return super(StockPickingType, self).create(vals) + + def write(self, vals): + if 'sequence_code' in vals: + for picking_type in self.filtered(lambda p: p.l10n_it_ddt_sequence_id): + warehouse = picking_type.warehouse_id.id if 'warehouse_id' not in vals else vals['warehouse_ids'] + ir_seq_name, ir_seq_prefix = self._get_dtt_ir_seq_vals(warehouse, vals['sequence_code']) + picking_type.l10n_it_ddt_sequence_id.write({ + 'name': ir_seq_name, + 'prefix': ir_seq_prefix, + }) + return super(StockPickingType, self).write(vals)
\ No newline at end of file |
