Initial commit: L10N_Europe packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:52 +02:00
commit 9803722600
2377 changed files with 380711 additions and 0 deletions

View file

@ -0,0 +1,65 @@
# l10n_it_stock_ddt
Documento di Trasporto (DDT)
Whenever goods are transferred between A and B, the DDT serves
as a legitimation e.g. when the police would stop you.
When you want to print an outgoing picking in an Italian company,
it will print you the DDT instead. It is like the delivery
slip, but it also contains the value of the product,
the transportation reason, the carrier, ... which make it a DDT.
We also use a separate sequence for the DDT as the number should not
have any gaps and should only be applied at the moment the goods are sent.
When invoices are related to their sale order and the sale order with the
delivery, the system will automatically calculate the linked DDTs for every
invoice line to export in the FatturaPA XML.
## Installation
```bash
pip install odoo-bringout-oca-ocb-l10n_it_stock_ddt
```
## Dependencies
This addon depends on:
- l10n_it_edi
- delivery
- stock_account
## Manifest Information
- **Name**: l10n_it_stock_ddt
- **Version**: 0.1
- **Category**: Accounting/Localizations/EDI
- **License**: LGPL-3
- **Installable**: False
## Source
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `l10n_it_stock_ddt`.
## License
This package maintains the original LGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph L10n_it_stock_ddt Module - l10n_it_stock_ddt
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for l10n_it_stock_ddt. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,7 @@
# Dependencies
This addon depends on:
- [l10n_it_edi](../../odoo-bringout-oca-ocb-l10n_it_edi)
- [delivery](../../odoo-bringout-oca-ocb-delivery)
- [stock_account](../../odoo-bringout-oca-ocb-stock_account)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon l10n_it_stock_ddt or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-ocb-l10n_it_stock_ddt"
# or
uv pip install odoo-bringout-oca-ocb-l10n_it_stock_ddt"
```

View file

@ -0,0 +1,15 @@
# Models
Detected core models and extensions in l10n_it_stock_ddt.
```mermaid
classDiagram
class account_edi_format
class account_move
class stock_picking
class stock_picking_type
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: l10n_it_stock_ddt. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon l10n_it_stock_ddt
- License: LGPL-3

View file

@ -0,0 +1,23 @@
# Reports
Report definitions and templates in l10n_it_stock_ddt.
```mermaid
classDiagram
```
## Available Reports
### PDF/Document Reports
- **DDT report** (PDF/Print)
## Report Files
- **l10n_it_ddt_report.xml** (XML template/definition)
## Notes
- Named reports above are accessible through Odoo's reporting menu
- Python files define report logic and data processing
- XML files contain report templates, definitions, and formatting
- Reports are integrated with Odoo's printing and email systems

View file

@ -0,0 +1,8 @@
# Security
This module does not define custom security rules or access controls beyond Odoo defaults.
Default Odoo security applies:
- Base user access through standard groups
- Model access inherited from dependencies
- No custom row-level security rules

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon l10n_it_stock_ddt
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from . import models
from odoo import api, SUPERUSER_ID
def _create_picking_seq(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
ptypes = env['stock.picking.type'].search([('code', '=', 'outgoing'), ('warehouse_id', '!=', False)])
for ptype in ptypes:
wh = ptype.warehouse_id
ptype.l10n_it_ddt_sequence_id = env['ir.sequence'].create({
'name': wh.name + ' ' + ' Sequence ' + ' ' + ptype.sequence_code,
'prefix': wh.code + '/' + ptype.sequence_code + '/DDT', 'padding': 5,
'company_id': wh.company_id.id,
'implementation': 'no_gap',
}).id

View file

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
{
'name': "l10n_it_stock_ddt",
'icon': '/l10n_it/static/description/icon.png',
'website': 'https://www.odoo.com',
'category': 'Accounting/Localizations/EDI',
'version': '0.1',
'description': """
Documento di Trasporto (DDT)
Whenever goods are transferred between A and B, the DDT serves
as a legitimation e.g. when the police would stop you.
When you want to print an outgoing picking in an Italian company,
it will print you the DDT instead. It is like the delivery
slip, but it also contains the value of the product,
the transportation reason, the carrier, ... which make it a DDT.
We also use a separate sequence for the DDT as the number should not
have any gaps and should only be applied at the moment the goods are sent.
When invoices are related to their sale order and the sale order with the
delivery, the system will automatically calculate the linked DDTs for every
invoice line to export in the FatturaPA XML.
""",
'depends': ['l10n_it_edi', 'delivery', 'stock_account'],
'data': [
'report/l10n_it_ddt_report.xml',
'views/stock_picking_views.xml',
'views/account_invoice_views.xml',
'data/l10n_it_ddt_template.xml',
],
'auto_install': True,
'post_init_hook': '_create_picking_seq',
'license': 'LGPL-3',
}

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="my_view_name" inherit_id="l10n_it_edi.account_invoice_it_FatturaPA_export">
<xpath expr='//DatiDDT' position="after">
<t t-if="ddt_dict and not record.l10n_it_ddt_id" t-foreach="ddt_dict" t-as="picking">
<DatiDDT>
<NumeroDDT t-if="picking.l10n_it_ddt_number" t-esc="format_alphanumeric(picking.l10n_it_ddt_number[-20:])"/>
<DataDDT t-esc="format_date(picking.date_done)"/>
<t t-if="len(ddt_dict) > 1" t-foreach="ddt_dict[picking]" t-as="line_ref">
<RiferimentoNumeroLinea t-esc="line_ref"/>
</t>
</DatiDDT>
</t>
</xpath>
</template>
</odoo>

View file

@ -0,0 +1,315 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_it_stock_ddt
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-11 14:02+0000\n"
"PO-Revision-Date: 2022-01-11 14:02+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_it_stock_ddt
#: model:ir.actions.report,print_report_name:l10n_it_stock_ddt.action_report_ddt
msgid ""
"'DDT - %s - %s' % (object.partner_id.name or '', object.l10n_it_ddt_number)"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<b>Note:</b>"
msgstr "<b>Nota:</b>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<b>Total:</b>"
msgstr "<b>Totale:</b>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<b>Transportation Method Details: </b>"
msgstr "<b>Dettagli metodo di trasporto</b>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<span><strong>Customer Address:</strong></span>"
msgstr "<span><strong>Indirizzo Cliente:</strong></span>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<span><strong>Warehouse Address:</strong></span>"
msgstr "<span><strong>Indirizzo Magazzino:</strong></span>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Carrier Signature</strong>"
msgstr "<strong>Firma Corriere</strong>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Company Signature</strong>"
msgstr "<strong>Firma Azienda</strong>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Customer Signature</strong>"
msgstr "<strong>Firma Cliente</strong>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Product</strong>"
msgstr "<strong>Prodotto</strong>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Quantity</strong>"
msgstr "<strong>Quantità</strong>"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Total Value</strong>"
msgstr "<strong>Valore Totale</strong>"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__attemped_sale
msgid "Attempted Sale"
msgstr "Tentata Vendita"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Carrier"
msgstr "Corriere"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Carrier Condition"
msgstr "Termini di Resa"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_country_code
msgid "Country Code"
msgstr "Codice Nazione"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_method__courier
msgid "Courier service"
msgstr "Servizio Corriere"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.view_picking_form_inherit_l10n_it_ddt
msgid "DDT Information"
msgstr "Informazioni DDT"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_ddt_number
msgid "DDT Number"
msgstr "Numero DDT"
#. module: l10n_it_stock_ddt
#: model:ir.actions.report,name:l10n_it_stock_ddt.action_report_ddt
msgid "DDT report"
msgstr "DDT"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.account_invoice_view_form_inherit_ddt
msgid "DDTs"
msgstr "DDT"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__display_name
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Documento di Trasporto"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__evaluation
msgid "Evaluation"
msgstr "Conto Visione"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__gift
msgid "Gift"
msgstr "Omaggio"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Gross Weight (kg)"
msgstr "Peso lordo (kg)"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__id
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__id
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type__id
msgid "ID"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model,name:l10n_it_stock_ddt.model_account_move
msgid "Journal Entry"
msgstr "Registrazione contabile"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_bank_statement_line__l10n_it_ddt_ids
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__l10n_it_ddt_ids
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_payment__l10n_it_ddt_ids
msgid "L10N It Ddt"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_bank_statement_line__l10n_it_ddt_count
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__l10n_it_ddt_count
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_payment__l10n_it_ddt_count
msgid "L10N It Ddt Count"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type__l10n_it_ddt_sequence_id
msgid "L10N It Ddt Sequence"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move____last_update
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking____last_update
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: l10n_it_stock_ddt
#: code:addons/l10n_it_stock_ddt/models/account_invoice.py:0
#, python-format
msgid "Linked deliveries"
msgstr "Spedizioni associate"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__loaned_use
msgid "Loaned for Use"
msgstr "Prestito d'uso"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Order"
msgstr "Ordine"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__outsourcing
msgid "Outsourcing"
msgstr "Conto Lavorazione"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_parcels
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Parcels"
msgstr "Colli"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Picking Number"
msgstr "Numero prelievo"
#. module: l10n_it_stock_ddt
#: model:ir.model,name:l10n_it_stock_ddt.model_stock_picking_type
msgid "Picking Type"
msgstr "Tipologia prelievo"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.view_picking_form_inherit_l10n_it_ddt
msgid "Print"
msgstr "Stampa"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_method__recipient
msgid "Recipient"
msgstr "Destinatario"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__repair
msgid "Repair"
msgstr "Conto Riparazione"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__sale
msgid "Sale"
msgstr "Vendita"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_method__sender
msgid "Sender"
msgstr "Mittente"
#. module: l10n_it_stock_ddt
#: code:addons/l10n_it_stock_ddt/models/stock_picking.py:0
#, python-format
msgid "Sequence"
msgstr "Sequenza"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Shipping Date"
msgstr "Data transporto"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__substitution
msgid "Returned goods"
msgstr "Reso"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,help:l10n_it_stock_ddt.field_stock_picking__l10n_it_country_code
msgid ""
"The ISO country code in two chars. \n"
"You can use this field for quick search."
msgstr ""
"Il codice ISO nazionale in 2 caratteri. \n"
"Puoi usare questo campo per la ricerca veloce."
#. module: l10n_it_stock_ddt
#: model:ir.model,name:l10n_it_stock_ddt.model_stock_picking
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__transfer
msgid "Transfer"
msgstr "Trasferimento"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_transport_method
msgid "Transport Method"
msgstr "Metodo di trasporto"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_transport_method_details
msgid "Transport Note"
msgstr "Nota di trasporto"
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_transport_reason
msgid "Transport Reason"
msgstr "Ragione trasporto"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Transportation Method"
msgstr "Metodo di trasporto"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Transportation Reason"
msgstr "Ragione trasporto"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "VAT"
msgstr "Pta IVA"
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "VAT:"
msgstr "Pta IVA:"

View file

@ -0,0 +1,314 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_it_stock_ddt
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-10 15:18+0000\n"
"PO-Revision-Date: 2022-01-10 15:18+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_it_stock_ddt
#: model:ir.actions.report,print_report_name:l10n_it_stock_ddt.action_report_ddt
msgid ""
"'DDT - %s - %s' % (object.partner_id.name or '', object.l10n_it_ddt_number)"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<b>Note:</b>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<b>Total:</b>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<b>Transportation Method Details: </b>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<span><strong>Customer Address:</strong></span>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<span><strong>Warehouse Address:</strong></span>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Carrier Signature</strong>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Company Signature</strong>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Customer Signature</strong>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Product</strong>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Quantity</strong>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "<strong>Total Value</strong>"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__attemped_sale
msgid "Attempted Sale"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Carrier"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Carrier Condition"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_country_code
msgid "Country Code"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_method__courier
msgid "Courier service"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.view_picking_form_inherit_l10n_it_ddt
msgid "DDT Information"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_ddt_number
msgid "DDT Number"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.actions.report,name:l10n_it_stock_ddt.action_report_ddt
msgid "DDT report"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.account_invoice_view_form_inherit_ddt
msgid "DDTs"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__display_name
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type__display_name
msgid "Display Name"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Documento di Trasporto"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__evaluation
msgid "Evaluation"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__gift
msgid "Gift"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Gross Weight (kg)"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__id
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__id
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type__id
msgid "ID"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model,name:l10n_it_stock_ddt.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_bank_statement_line__l10n_it_ddt_ids
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__l10n_it_ddt_ids
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_payment__l10n_it_ddt_ids
msgid "L10N It Ddt"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_bank_statement_line__l10n_it_ddt_count
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move__l10n_it_ddt_count
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_payment__l10n_it_ddt_count
msgid "L10N It Ddt Count"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type__l10n_it_ddt_sequence_id
msgid "L10N It Ddt Sequence"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_account_move____last_update
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking____last_update
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking_type____last_update
msgid "Last Modified on"
msgstr ""
#. module: l10n_it_stock_ddt
#: code:addons/l10n_it_stock_ddt/models/account_invoice.py:0
#, python-format
msgid "Linked deliveries"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__loaned_use
msgid "Loaned for Use"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Order"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__outsourcing
msgid "Outsourcing"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_parcels
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Parcels"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Picking Number"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model,name:l10n_it_stock_ddt.model_stock_picking_type
msgid "Picking Type"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.view_picking_form_inherit_l10n_it_ddt
msgid "Print"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_method__recipient
msgid "Recipient"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__repair
msgid "Repair"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__sale
msgid "Sale"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_method__sender
msgid "Sender"
msgstr ""
#. module: l10n_it_stock_ddt
#: code:addons/l10n_it_stock_ddt/models/stock_picking.py:0
#: code:addons/l10n_it_stock_ddt/models/stock_picking.py:0
#, python-format
msgid "Sequence"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Shipping Date"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__substitution
msgid "Returned goods"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,help:l10n_it_stock_ddt.field_stock_picking__l10n_it_country_code
msgid ""
"The ISO country code in two chars. \n"
"You can use this field for quick search."
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model,name:l10n_it_stock_ddt.model_stock_picking
#: model:ir.model.fields.selection,name:l10n_it_stock_ddt.selection__stock_picking__l10n_it_transport_reason__transfer
msgid "Transfer"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_transport_method
msgid "Transport Method"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_transport_method_details
msgid "Transport Note"
msgstr ""
#. module: l10n_it_stock_ddt
#: model:ir.model.fields,field_description:l10n_it_stock_ddt.field_stock_picking__l10n_it_transport_reason
msgid "Transport Reason"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Transportation Method"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "Transportation Reason"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "VAT"
msgstr ""
#. module: l10n_it_stock_ddt
#: model_terms:ir.ui.view,arch_db:l10n_it_stock_ddt.report_ddt_view
msgid "VAT:"
msgstr ""

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import stock_picking
from . import account_invoice
from . import account_edi_format

View file

@ -0,0 +1,30 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class AccountEdiFormat(models.Model):
_inherit = 'account.edi.format'
def _l10n_it_invoice_is_direct(self, invoice):
""" An invoice is only direct if the Transport Documents are all done the same day as the invoice. """
for ddt in invoice.l10n_it_ddt_ids:
if not ddt.date_done or ddt.date_done.date() != invoice.invoice_date:
return False
return True
def _l10n_it_get_invoice_features_for_document_type_selection(self, invoice):
res = super()._l10n_it_get_invoice_features_for_document_type_selection(invoice)
res['direct_invoice'] = self._l10n_it_invoice_is_direct(invoice)
return res
def _l10n_it_document_type_mapping(self):
""" Deferred invoices (not direct) require TD24 FatturaPA Document Type. """
res = super()._l10n_it_document_type_mapping()
for document_type, infos in res.items():
if document_type == 'TD07':
continue
infos['direct_invoice'] = True
res['TD24'] = dict(move_types=['out_invoice'], import_type='in_invoice', direct_invoice=False)
return res

View file

@ -0,0 +1,86 @@
# -*- 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: l.display_type not in ('line_note', 'line_section')):
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' and m.picking_type_id.code == 'outgoing')
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, l.move_id.id))
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, m.id)):
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.account_fiscal_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

View file

@ -0,0 +1,96 @@
# -*- 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', 'Returned goods'),
('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_ddt_number = fields.Char('DDT Number', readonly=True)
l10n_it_show_print_ddt_button = fields.Boolean(compute="_compute_l10n_it_show_print_ddt_button")
@api.depends('country_code',
'picking_type_code',
'state',
'is_locked',
'move_ids',
'location_id',
'location_dest_id')
def _compute_l10n_it_show_print_ddt_button(self):
# Enable printing the DDT for done outgoing shipments
# or dropshipping (picking going from supplier to customer)
for picking in self:
picking.l10n_it_show_print_ddt_button = (
picking.country_code == 'IT'
and picking.state == 'done'
and picking.is_locked
and (picking.picking_type_code == 'outgoing'
or (
picking.move_ids_without_package
and picking.move_ids_without_package[0].partner_id
and picking.location_id.usage == 'supplier'
and picking.location_dest_id.usage == 'customer'
)
)
)
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_create_multi
def create(self, vals_list):
for vals in vals_list:
company = self.env['res.company'].browse(vals.get('company_id', False)) or self.env.company
if company.country_id.code == 'IT' and vals.get('code') == 'outgoing' and ('l10n_it_ddt_sequence_id' not in vals or not vals['l10n_it_ddt_sequence_id']):
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': company.id,
'implementation': 'no_gap',
}).id
return super().create(vals_list)
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)

View file

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="report_ddt_view">
<t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context(lang=lang)"/>
<t t-if="o.move_ids_without_package and o.move_ids_without_package[0].partner_id and o.location_dest_id.usage == 'customer' and o.location_id.usage == 'supplier'">
<t t-set="delivery_from" t-value="o.partner_id"/>
<t t-set="delivery_to" t-value="o.move_ids_without_package[0].partner_id"/>
</t>
<t t-elif="o.picking_type_id.warehouse_id.partner_id">
<t t-set="delivery_from" t-value="o.picking_type_id.warehouse_id.partner_id"/>
<t t-set="delivery_to" t-value="o.partner_id"/>
</t>
<t t-else="">
<t t-set="delivery_from" t-value="o.company_id.partner_id"/>
<t t-set="delivery_to" t-value="o.partner_id"/>
</t>
<div class="page">
<div class="row">
<div class="col-6">
<span><strong>Warehouse Address:</strong></span>
<div t-esc="delivery_from"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
<p t-if="delivery_from.vat">VAT: <span t-field="delivery_from.vat"/></p>
</div>
<div class="col-5 offset-1">
<div>
<span><strong>Customer Address:</strong></span>
<div t-esc="delivery_to"
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "vat"], "no_marker": True, "phone_icons": True}'/>
</div>
</div>
</div>
<div class="mt16"/>
<div class="mt64"/>
<div>
<h1>Documento di Trasporto <span t-esc="o.l10n_it_ddt_number"/></h1>
</div>
<div class="clearfix"/>
<div class="mb32"/>
<div class="row">
<div class="col-6">
<table class="table table-bordered">
<tbody>
<tr>
<td>Transportation Reason</td>
<td><span t-field="o.l10n_it_transport_reason"/></td>
</tr>
<tr>
<td>Transportation Method</td>
<td><span t-field="o.l10n_it_transport_method"/></td>
</tr>
<tr>
<td>Carrier Condition</td>
<td><span t-field="o.sale_id.incoterm.name"/></td>
</tr>
<tr>
<td>Carrier</td>
<td><span t-field="o.carrier_id"/></td>
</tr>
</tbody>
</table>
<div t-if="o.l10n_it_transport_method_details">
<b>Transportation Method Details: </b>
<span t-field="o.l10n_it_transport_method_details"/>
</div>
</div>
<div class="col-5 offset-1">
<table class="table table-bordered">
<tbody>
<tr>
<td>Order</td>
<td><span t-field="o.origin"/></td>
</tr>
<tr>
<td>Picking Number</td>
<td><span t-field="o.name"/></td>
</tr>
<tr>
<td>Shipping Date</td>
<td><span t-field="o.date_done"/></td>
</tr>
<tr>
<td>Gross Weight (kg)</td>
<td><span t-field="o.shipping_weight"/></td>
</tr>
<tr>
<td>Parcels</td>
<td><span t-field="o.l10n_it_parcels"/></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="mt64"/>
<div t-if="o.note"><b>Note:</b> <span t-field="o.note"/></div>
<div class="mt64"/>
<div class="mt64"/>
<table class="table table-sm" name="document_details">
<t t-set="display_discount" t-value="any(m.sale_line_id.discount for m in o.move_ids)"/>
<thead>
<tr>
<th><strong>Product</strong></th>
<th><strong>Quantity</strong></th>
<t t-if="display_discount">
<th><strong>Disc.%</strong></th>
</t>
<th><strong>Total Value</strong></th>
</tr>
</thead>
<tbody>
<t t-set="total_value" t-value="0"/>
<t t-foreach="o.move_ids" t-as="move">
<t t-if="move.quantity_done">
<tr>
<td>
<span t-field="move.product_id"/>
</td>
<td>
<span t-field="move.quantity_done"/>
<span t-field="move.product_uom" groups="uom.group_uom"/>
</td>
<td t-if="display_discount">
<span t-field="move.sale_line_id.discount"/>
</td>
<td>
<t t-if="move.sale_line_id">
<t t-set="lst_price" t-value="move.sale_line_id.price_reduce_taxinc * move.quantity_done"/>
</t>
<t t-else="">
<t t-set="lst_price" t-value="move.product_id.lst_price * move.product_uom._compute_quantity(move.quantity_done, move.product_id.uom_id)"/>
</t>
<span t-esc="lst_price" t-options='{"widget": "monetary", "display_currency": o.company_id.currency_id}'/>
<t t-set="total_value" t-value="total_value + lst_price"/>
</td>
</tr>
</t>
</t>
<tr>
<td t-if="display_discount">
</td>
<td>
</td>
<td style="text-align:right">
<b>Total:</b>
</td>
<td>
<span t-esc="total_value" t-options='{"widget": "monetary", "display_currency": o.company_id.currency_id}'/>
</td>
</tr>
</tbody>
</table>
<div class="mt64"/>
<div class="mt64"/>
<table class="table table-sm">
<thead>
<tr>
<th><span class="fa fa-pencil mt4"/><div class="ml4 d-inline-block"/><strong>Company Signature</strong></th>
<th><span class="fa fa-pencil mt4"/><div class="ml4 d-inline-block"/><strong>Carrier Signature</strong></th>
<th><span class="fa fa-pencil mt4"/><div class="ml4 d-inline-block"/><strong>Customer Signature</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="col">
</div>
</td>
<td>
<div class="col">
</div>
</td>
<td>
<div class="col">
</div>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</template>
<template id="report_ddt">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-set="lang" t-value="o._get_report_lang()"/>
<t t-call="l10n_it_stock_ddt.report_ddt_view" t-lang="lang"/>
</t>
</t>
</template>
<record id="action_report_ddt" model="ir.actions.report">
<field name="name">DDT report</field>
<field name="model">stock.picking</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_it_stock_ddt.report_ddt</field>
<field name="report_file">report_ddt</field>
<field name="print_report_name">'DDT - %s - %s' % (object.partner_id.name or '', object.l10n_it_ddt_number)</field>
</record>
</odoo>

View file

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_ddt
from . import test_edi

View file

@ -0,0 +1,130 @@
<p:FatturaElettronica xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xsi:schemaLocation="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2 http://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/Schema_del_file_xml_FatturaPA_versione_1.2.xsd" versione="FPR12">
<FatturaElettronicaHeader>
<DatiTrasmissione>
<IdTrasmittente>
<IdPaese>IT</IdPaese>
<IdCodice>01234560157</IdCodice>
</IdTrasmittente>
<ProgressivoInvio>V202000001</ProgressivoInvio>
<FormatoTrasmissione>FPR12</FormatoTrasmissione>
<CodiceDestinatario>0000000</CodiceDestinatario>
<ContattiTrasmittente>
</ContattiTrasmittente>
</DatiTrasmissione>
<CedentePrestatore>
<DatiAnagrafici>
<IdFiscaleIVA>
<IdPaese>IT</IdPaese>
<IdCodice>01234560157</IdCodice>
</IdFiscaleIVA>
<CodiceFiscale>01234560157</CodiceFiscale>
<Anagrafica>
<Denominazione>company_2_data</Denominazione>
</Anagrafica>
<RegimeFiscale>RF01</RegimeFiscale>
</DatiAnagrafici>
<Sede>
<Indirizzo>1234 Test Street </Indirizzo>
<CAP>12345</CAP>
<Comune>Prova</Comune>
<Nazione>IT</Nazione>
</Sede>
</CedentePrestatore>
<CessionarioCommittente>
<DatiAnagrafici>
<IdFiscaleIVA>
<IdPaese>IT</IdPaese>
<IdCodice>00465840031</IdCodice>
</IdFiscaleIVA>
<CodiceFiscale>93026890017</CodiceFiscale>
<Anagrafica>
<Denominazione>Alessi</Denominazione>
</Anagrafica>
</DatiAnagrafici>
<Sede>
<Indirizzo>Via Privata Alessi 6 </Indirizzo>
<CAP>28887</CAP>
<Comune>Milan</Comune>
<Nazione>IT</Nazione>
</Sede>
</CessionarioCommittente>
</FatturaElettronicaHeader>
<FatturaElettronicaBody>
<DatiGenerali>
<DatiGeneraliDocumento>
<TipoDocumento>TD24</TipoDocumento>
<Divisa>EUR</Divisa>
<Data>2020-02-03</Data>
<Numero>INV/2020/00001</Numero>
<ImportoTotaleDocumento>2427.80</ImportoTotaleDocumento>
</DatiGeneraliDocumento>
<DatiDDT>
<NumeroDDT>compa/OUT/DDT00001</NumeroDDT>
<DataDDT>2020-02-02</DataDDT>
<RiferimentoNumeroLinea>2</RiferimentoNumeroLinea>
<RiferimentoNumeroLinea>3</RiferimentoNumeroLinea>
</DatiDDT>
<DatiDDT>
<NumeroDDT>compa/OUT/DDT00002</NumeroDDT>
<DataDDT>2020-02-02</DataDDT>
<RiferimentoNumeroLinea>2</RiferimentoNumeroLinea>
<RiferimentoNumeroLinea>3</RiferimentoNumeroLinea>
</DatiDDT>
</DatiGenerali>
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<CodiceArticolo>
<CodiceTipo>INTERNAL</CodiceTipo>
<CodiceValore>PRE-PAID</CodiceValore>
</CodiceArticolo>
<Descrizione>product_service_order</Descrizione>
<Quantita>5.00</Quantita>
<UnitaMisura>Hours</UnitaMisura>
<PrezzoUnitario>90.000000</PrezzoUnitario>
<PrezzoTotale>450.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<CodiceArticolo>
<CodiceTipo>INTERNAL</CodiceTipo>
<CodiceValore>FURN_9999</CodiceValore>
</CodiceArticolo>
<Descrizione>product_order_no</Descrizione>
<Quantita>5.00</Quantita>
<PrezzoUnitario>280.000000</PrezzoUnitario>
<PrezzoTotale>1400.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>3</NumeroLinea>
<CodiceArticolo>
<CodiceTipo>INTERNAL</CodiceTipo>
<CodiceValore>FURN_7777</CodiceValore>
</CodiceArticolo>
<Descrizione>product_delivery_no</Descrizione>
<Quantita>2.00</Quantita>
<PrezzoUnitario>70.000000</PrezzoUnitario>
<PrezzoTotale>140.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>1990.00</ImponibileImporto>
<Imposta>437.80</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
<DatiPagamento>
<CondizioniPagamento>TP02</CondizioniPagamento>
<DettaglioPagamento>
<ModalitaPagamento>MP05</ModalitaPagamento>
<DataScadenzaPagamento>2020-02-03</DataScadenzaPagamento>
<ImportoPagamento>2427.80</ImportoPagamento>
<CodicePagamento>INV/2020/00001</CodicePagamento>
</DettaglioPagamento>
</DatiPagamento>
<Allegati/>
</FatturaElettronicaBody>
</p:FatturaElettronica>

View file

@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.sale.tests.common import TestSaleCommon
from odoo.tests import tagged, Form
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestDDT(TestSaleCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_it.l10n_it_chart_template_generic'):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.company_data['company'].write({
'vat':"IT12345670017",
'country_id': cls.env.ref('base.it').id,
'l10n_it_codice_fiscale': '01234560157',
'l10n_it_tax_system': 'RF01',
'street': 'Via Giovanni Maria Platina 66',
'zip': '26100',
'city': 'Cremona',
})
cls.env['res.partner.bank'].create({
'acc_number': 'IT60X0542811101000000123456',
'partner_id': cls.company_data['company'].partner_id.id,
})
cls.partner_a.write({
'street': 'Piazza Guglielmo Marconi 5',
'zip': '26100',
'city': 'Cremona',
'country_id': cls.env.ref('base.it').id,
'vat': 'IT12345670124'
})
settings = cls.env['res.config.settings'].create({})
if hasattr(settings, 'button_create_proxy_user'):
# Needed when `l10n_it_edi_sdiscoop` is installed
settings.button_create_proxy_user()
@classmethod
def setup_company_data(cls, company_name, **kwargs):
return super().setup_company_data(company_name, **{
**kwargs,
'country_id': cls.env.ref('base.it').id,
})
def test_ddt_flow(self):
"""
We confirm a sale order and handle its delivery partially.
This should have created a DDT number and when we generate and the invoice,
the delivery should be linked to it as DDT.
"""
self.so = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'partner_invoice_id': self.partner_a.id,
'partner_shipping_id': self.partner_a.id,
'order_line': [(0, 0, {'name': p.name,
'product_id': p.id,
'product_uom_qty': 5,
'product_uom': p.uom_id.id,
'price_unit': p.list_price,
'tax_id': self.company_data['default_tax_sale']})
for p in (
self.company_data['product_order_no'],
self.company_data['product_service_delivery'],
self.company_data['product_service_order'],
self.company_data['product_delivery_no'],
)],
'pricelist_id': self.company_data['default_pricelist'].id,
'picking_policy': 'direct',
})
self.so.action_confirm()
# deliver partially
pick = self.so.picking_ids
pick.move_ids.write({'quantity_done': 1})
wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
self.assertTrue(pick.l10n_it_ddt_number, 'The outgoing picking should have a DDT number')
self.inv1 = self.so._create_invoices()
self.inv1.action_post()
self.assertEqual(self.inv1.l10n_it_ddt_ids.ids, pick.ids, 'DDT should be linked to the invoice')
# deliver partially
pickx1 = self.so.picking_ids.filtered(lambda p: p.state != 'done')
pickx1.move_ids.write({'quantity_done': 1})
wiz_act = pickx1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# and again
pickx2 = self.so.picking_ids.filtered(lambda p: p.state != 'done')
pickx2.move_ids.write({'quantity_done': 2})
wiz_act = pickx2.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
self.inv2 = self.so._create_invoices()
self.inv2.action_post()
self.inv2.flush_model()
self.inv2.invalidate_model()
self.assertIn(pickx1, self.inv2.l10n_it_ddt_ids)
self.assertIn(pickx2, self.inv2.l10n_it_ddt_ids)
# FIXME this check only worked because of a strange cache behavior
# But is consistently broken after recent cleanings in sale
# with the flush & invalidate, it always breaks, even without the cleanings
# self.assertEqual(self.inv2.l10n_it_ddt_ids.ids, (pickx1 | pickx2).ids, 'DDTs should be linked to the invoice')
def test_ddt_flow_2(self):
"""
Test that the link between the invoice lines and the deliveries linked to the invoice
through the link with the sale order is calculated correctly.
"""
so = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [(0, 0, {
'product_id': self.product_a.id,
'product_uom_qty': 3,
'product_uom': self.product_a.uom_id.id,
'price_unit': self.product_a.list_price,
'tax_id': self.company_data['default_tax_sale']
}
)],
'pricelist_id': self.company_data['default_pricelist'].id,
'picking_policy': 'direct',
})
so.action_confirm()
# deliver partially
picking_1 = so.picking_ids
picking_1.move_ids.write({'quantity_done': 1})
wiz_act = picking_1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
invoice_1 = so._create_invoices()
invoice_1.invoice_line_ids[0].quantity = 1.0
invoice_1.action_post()
picking_2 = so.picking_ids.filtered(lambda p: p.state != 'done')
picking_2.move_ids.write({'quantity_done': 2})
picking_2.button_validate()
invoice_2 = so._create_invoices()
invoice_2.action_post()
# Invalidate the cache to ensure the lines will be fetched in the right order.
picking_2.invalidate_model()
self.assertEqual(invoice_1.l10n_it_ddt_ids.ids, picking_1.ids, 'DDT picking_1 should be linked to the invoice_1')
self.assertEqual(invoice_2.l10n_it_ddt_ids.ids, picking_2.ids, 'DDT picking_2 should be linked to the invoice_2')

View file

@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from freezegun import freeze_time
from odoo import tools
from odoo.tests import tagged, Form
from odoo.addons.l10n_it_edi.tests.common import TestItEdi
_logger = logging.getLogger(__name__)
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiDDT(TestItEdi):
@classmethod
def setUpClass(cls):
super().setUpClass()
# UoM
uom_unit = cls.env.ref('uom.product_uom_unit')
uom_hour = cls.env.ref('uom.product_uom_hour')
# Tax
cls.tax_22 = cls.env['account.tax'].with_company(cls.company).create({
'name': '22% test tax',
'amount': 22.0,
'amount_type': 'percent'
})
# Products and pricelist
cls.default_pricelist = cls.env['product.pricelist'].with_company(cls.company).create({
'name': 'default_pricelist',
'currency_id': cls.company.currency_id.id,
})
product_category = cls.env['product.category'].with_company(cls.company).create({'name': 'Test category'})
cls.products = cls.env['product.product'].with_company(cls.company).create([
{
'name': 'product_service_delivery',
'categ_id': product_category.id,
'standard_price': 200.0,
'list_price': 180.0,
'type': 'service',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'default_code': 'SERV_DEL',
'invoice_policy': 'delivery',
'taxes_id': [(6, 0, [])],
'supplier_taxes_id': [(6, 0, [])],
}, {
'name': 'product_service_order',
'categ_id': product_category.id,
'standard_price': 40.0,
'list_price': 90.0,
'type': 'service',
'uom_id': uom_hour.id,
'uom_po_id': uom_hour.id,
'description': 'Example of product to invoice on order',
'default_code': 'PRE-PAID',
'invoice_policy': 'order',
'taxes_id': [(6, 0, [])],
'supplier_taxes_id': [(6, 0, [])],
}, {
'name': 'product_order_no',
'categ_id': product_category.id,
'standard_price': 235.0,
'list_price': 280.0,
'type': 'consu',
'weight': 0.01,
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'default_code': 'FURN_9999',
'invoice_policy': 'order',
'expense_policy': 'no',
'taxes_id': [(6, 0, [])],
'supplier_taxes_id': [(6, 0, [])],
}, {
'name': 'product_delivery_no',
'categ_id': product_category.id,
'standard_price': 55.0,
'list_price': 70.0,
'type': 'consu',
'weight': 0.01,
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'default_code': 'FURN_7777',
'invoice_policy': 'delivery',
'expense_policy': 'no',
'taxes_id': [(6, 0, [])],
'supplier_taxes_id': [(6, 0, [])],
}
])
@classmethod
def setup_company_data(cls, company_name, **kwargs):
return super().setup_company_data(company_name, **{
**kwargs,
'country_id': cls.env.ref('base.it').id,
})
def test_deferred_invoice(self):
""" Create a sale order with multiple DDTs, and create an invoice with a later date.
The export has to have the TipoDocumento TD24 for Deferred Invoice.
"""
# Create sale order
with freeze_time('2020-02-02 18:00'):
self.sale_order = self.env['sale.order'].with_company(self.company).create({
'partner_id': self.italian_partner_a.id,
'partner_invoice_id': self.italian_partner_a.id,
'partner_shipping_id': self.italian_partner_a.id,
'order_line': [
(0, 0, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': 5,
'product_uom': product.uom_id.id,
'price_unit': product.list_price,
'tax_id': self.tax_22
}) for product in self.products
],
'pricelist_id': self.default_pricelist.id,
'picking_policy': 'direct',
})
self.sale_order.action_confirm()
# Create two pickings, so 2 DDTs
for dummy in range(2):
self._create_delivery(self.sale_order, 1)
# Create one invoice
with freeze_time('2020-02-03 09:00'):
deferred_invoice = self.sale_order._create_invoices()
deferred_invoice.action_post()
# Check the XML output of the invoice
invoice_xml = self.edi_format._l10n_it_edi_export_invoice_as_xml(deferred_invoice)
expected_xml = self._get_stock_ddt_test_file_content("deferred_invoice.xml")
result = self._cleanup_etree(invoice_xml, {"//DatiGeneraliDocumento/Numero": "<Numero/>",})
expected = self._cleanup_etree(expected_xml, {"//DatiGeneraliDocumento/Numero": "<Numero/>",})
self.assertXmlTreeEqual(result, expected)
def _create_delivery(self, sale_order, qty=1):
""" Create a picking of a limited quantity and create a backorder """
pickings = sale_order.picking_ids.filtered(lambda picking: picking.state != 'done')
pickings.move_ids.write({'quantity_done': qty})
wizard_action = pickings.button_validate()
context = wizard_action['context']
wizard = Form(self.env['stock.backorder.confirmation'].with_context(context))
confirm_dialog = wizard.save()
confirm_dialog.process()
@classmethod
def _get_stock_ddt_test_file_content(cls, filename):
""" Get the content of a test file inside this module """
path = 'l10n_it_stock_ddt/tests/expected_xmls/' + filename
with tools.file_open(path, mode='rb') as test_file:
return test_file.read()

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_invoice_view_form_inherit_ddt" model="ir.ui.view">
<field name="name">account.invoice.form.inherit.ddt</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="get_linked_ddts" type="object" class="oe_stat_button" icon="fa-calendar" attrs="{'invisible':[('l10n_it_ddt_count','=', 0)]}">
<field name="l10n_it_ddt_count" widget="statinfo" string="DDTs"/>
</button>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_picking_form_inherit_l10n_it_ddt" model="ir.ui.view">
<field name="name">stock.picking.form.l10n.it.ddt</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='do_print_picking']" position="after">
<field name="country_code" invisible="1"/>
<field name="l10n_it_show_print_ddt_button" invisible="1"/>
<button name="%(l10n_it_stock_ddt.action_report_ddt)d" type="action" string="Print"
attrs="{'invisible': [('l10n_it_show_print_ddt_button', '=', False)]}"
groups="base.group_user"/>
</xpath>
<xpath expr="//button[@name='%(stock.action_report_delivery)d']" position="attributes">
<attribute name="attrs">{'invisible': ['|', ('l10n_it_show_print_ddt_button', '=', True), '&amp;', ('picking_type_code', '=', 'outgoing'), ('country_code', '=', 'IT')]}</attribute>
</xpath>
<group name='carrier_data' position="after">
<group string="DDT Information" attrs="{'invisible': ['|', ('country_code', '!=', 'IT'), ('picking_type_code', '!=', 'outgoing')]}">
<field name="country_code" invisible="1"/>
<field name="l10n_it_ddt_number"/>
<field name="l10n_it_transport_reason"/>
<field name="l10n_it_transport_method"/>
<field name="l10n_it_transport_method_details"/>
<field name="l10n_it_parcels"/>
</group>
</group>
</field>
</record>
<record id="view_picking_search_inherit_l10n_it_ddt" model="ir.ui.view">
<field name="name">stock.picking.search.l10n.it.ddt</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
<field name="arch" type="xml">
<field name="origin" position="after">
<field name="l10n_it_ddt_number"/>
</field>
</field>
</record>
<record id="view_picking_tree_inherit_l10n_it_ddt" model="ir.ui.view">
<field name="name">stock.picking.tree.l10n.it.ddt</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<field name="origin" position="after">
<field name="l10n_it_ddt_number" optional="hide"/>
</field>
</field>
</record>
</odoo>

View file

@ -0,0 +1,44 @@
[project]
name = "odoo-bringout-oca-ocb-l10n_it_stock_ddt"
version = "16.0.0"
description = "l10n_it_stock_ddt - Odoo addon"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-l10n_it_edi>=16.0.0",
"odoo-bringout-oca-ocb-delivery>=16.0.0",
"odoo-bringout-oca-ocb-stock_account>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["l10n_it_stock_ddt"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]