Skip to content

Commit 94d5f9e

Browse files
committed
[14.0][MIG] sale_order_import_edifact & base_edifact
1 parent 43a569c commit 94d5f9e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2368
-0
lines changed

base_edifact/README.rst

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
============
2+
Base EDIFACT
3+
============
4+
5+
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6+
!! This file is generated by oca-gen-addon-readme !!
7+
!! changes will be overwritten. !!
8+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
9+
10+
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
11+
:target: https://odoo-community.org/page/development-status
12+
:alt: Alpha
13+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
14+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
15+
:alt: License: AGPL-3
16+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github
17+
:target: https://github.com/OCA/edi/tree/14.0/base_edifact
18+
:alt: OCA/edi
19+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
20+
:target: https://translation.odoo-community.org/projects/edi-14-0/edi-14-0-base_edifact
21+
:alt: Translate me on Weblate
22+
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
23+
:target: https://runbot.odoo-community.org/runbot/226/14.0
24+
:alt: Try me on Runbot
25+
26+
|badge1| |badge2| |badge3| |badge4| |badge5|
27+
28+
29+
This module contains methods to generate and parse EDIFACT/D96A files
30+
31+
.. IMPORTANT::
32+
This is an alpha version, the data model and design can change at any time without warning.
33+
Only for development or testing purpose, do not use in production.
34+
`More details on development status <https://odoo-community.org/page/development-status>`_
35+
36+
**Table of contents**
37+
38+
.. contents::
39+
:local:
40+
41+
Installation
42+
============
43+
44+
45+
This module requires 'pydifact' python library.
46+
47+
Configuration
48+
=============
49+
50+
Requires partner_identification_gln module to store GLN identifiers.
51+
52+
Partner identification code assigned by the European Article Numbering Association.
53+
54+
Use two identification categories:
55+
56+
- "GLN Identificatin Number". Partner identification like invoice or delivery address.
57+
- "GCP Identification Number". Global Company Prefix.
58+
59+
If GCP codes are needed in UNB interchange header.
60+
61+
If you need group partners, consider oca/partner-contact/partner_company_group module.
62+
63+
Usage
64+
=====
65+
66+
67+
This module doesn't do anything useful by itself, but it is used by several other modules:
68+
69+
* *sale_order_import_edifact* that imports EDIFACT/D96A sale orders.
70+
71+
Changelog
72+
=========
73+
74+
75+
14.0.1.0.0 (2023-04-13)
76+
~~~~~~~~~~~~~~~~~~~~~~~
77+
Strong migration from 12.0.1.0.1 because it's not working for Amazon vendor orders.
78+
79+
Bug Tracker
80+
===========
81+
82+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/edi/issues>`_.
83+
In case of trouble, please check there if your issue has already been reported.
84+
If you spotted it first, help us smashing it by providing a detailed and welcomed
85+
`feedback <https://github.com/OCA/edi/issues/new?body=module:%20base_edifact%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
86+
87+
Do not contact contributors directly about support or help with technical issues.
88+
89+
Credits
90+
=======
91+
92+
Authors
93+
~~~~~~~
94+
95+
* ALBA Software
96+
* PlanetaTIC
97+
98+
Contributors
99+
~~~~~~~~~~~~
100+
101+
* Rafa Morant <rmorant@albasoft.com> (www.albasoft.com)
102+
* Marc Poch <mpoch@planetatic.com>
103+
104+
Maintainers
105+
~~~~~~~~~~~
106+
107+
This module is maintained by the OCA.
108+
109+
.. image:: https://odoo-community.org/logo.png
110+
:alt: Odoo Community Association
111+
:target: https://odoo-community.org
112+
113+
OCA, or the Odoo Community Association, is a nonprofit organization whose
114+
mission is to support the collaborative development of Odoo features and
115+
promote its widespread use.
116+
117+
.. |maintainer-rmorant| image:: https://github.com/rmorant.png?size=40px
118+
:target: https://github.com/rmorant
119+
:alt: rmorant
120+
121+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
122+
123+
|maintainer-rmorant|
124+
125+
This module is part of the `OCA/edi <https://github.com/OCA/edi/tree/14.0/base_edifact>`_ project on GitHub.
126+
127+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

base_edifact/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

base_edifact/__manifest__.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2023 ALBA Software S.L.
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)
3+
4+
{
5+
"name": "Base EDIFACT",
6+
"summary": "UN/EDIFACT/D96A utilities using pydifact parser",
7+
"version": "14.0.1.0.0",
8+
"development_status": "Alpha",
9+
"category": "Tools",
10+
"website": "https://github.com/OCA/edi",
11+
"author": "ALBA Software, PlanetaTIC, Odoo Community Association (OCA)",
12+
"maintainers": ["rmorant"],
13+
"license": "AGPL-3",
14+
"application": False,
15+
"installable": True,
16+
# "preloadable": True,
17+
"external_dependencies": {
18+
"python": ["pydifact"],
19+
"bin": [],
20+
},
21+
"depends": [
22+
# "edi_party_data_oca"
23+
# for configuration
24+
"account",
25+
"partner_identification",
26+
"partner_identification_gln",
27+
],
28+
"data": [],
29+
}

base_edifact/models/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import edifact

base_edifact/models/edifact.py

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import datetime
2+
import logging
3+
4+
from pydifact.segmentcollection import Interchange, Message
5+
from pydifact.segments import Segment
6+
7+
from odoo import _, api, exceptions, models
8+
9+
from odoo.addons.edi_party_data_oca.utils import get_party_data_component
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
# https://github.com/nerdocs/pydifact
15+
class BasePydifact(models.AbstractModel):
16+
_name = "base.edifact"
17+
_description = "Generate and parse Edifact documents"
18+
19+
MAP_AGENCY_CODE_2_RES_PARTNER_NAMEREF = {"9": "gln"}
20+
CURRENCY_SYMBOLS = {
21+
"EUR": "€",
22+
"USD": "$",
23+
}
24+
PRODUCT_CODE_TYPES = {"EN": "EAN", "UP": "UPC", "SRV": "GTIN"}
25+
26+
@api.model
27+
def pydifact_import(self, names):
28+
classes = {
29+
"Segment": Segment,
30+
"Message": Message,
31+
"Interchange": Interchange,
32+
}
33+
return [classes.get(name, None) for name in names]
34+
35+
@api.model
36+
def pydifact_obj(self, docu):
37+
obj = []
38+
interchange = self._loads_edifact(docu)
39+
header_seg = interchange.get_header_segment()
40+
header_dict = dict()
41+
header_dict[header_seg.tag] = header_seg.elements
42+
obj.append(header_dict)
43+
for message in interchange.get_messages():
44+
segms = []
45+
msg = {
46+
"reference_number": message.reference_number,
47+
"type": message.type,
48+
"version": message.version,
49+
"identifier": message.identifier,
50+
"HEADER_TAG": message.HEADER_TAG,
51+
"FOOTER_TAG": message.FOOTER_TAG,
52+
"characters": str(message.characters),
53+
"extra_header_elements": message.extra_header_elements,
54+
"has_una_segment": message.has_una_segment,
55+
}
56+
logger.info(message)
57+
for segment in message.segments:
58+
logger.info(
59+
"Segment tag: {}, content: {}".format(segment.tag, segment.elements)
60+
)
61+
# segms.append((segment.tag, segment.elements))
62+
seg = dict()
63+
seg[segment.tag] = segment.elements
64+
segms.append(seg)
65+
msg["segments"] = segms
66+
obj.append(msg)
67+
return obj
68+
69+
@api.model
70+
def get_party_data(self, exchange_record, partner, raise_if_not_found=True):
71+
provider = get_party_data_component(exchange_record, partner)
72+
if not provider and raise_if_not_found:
73+
raise exceptions.UserError(_("No info provider found for party data"))
74+
return provider.get_party() if provider else None
75+
76+
@api.model
77+
def _loads_edifact(self, order_file):
78+
interchange = Interchange.from_str(order_file.decode())
79+
return interchange
80+
81+
@api.model
82+
def _get_msg_type(self, interchange):
83+
seg = interchange.get_segment("UNH")
84+
# MSG_TYPE, EDIFACT_MSG_TYPE_RELEASE
85+
return (seg[1][0], "{}{}".format(seg[1][1], seg[1][2]))
86+
87+
@api.model
88+
def map2odoo_date(self, dt):
89+
# '102'
90+
dtt = datetime.datetime.strptime(dt[1], "%Y%m%d")
91+
return dtt.date()
92+
93+
@api.model
94+
def map2odoo_partner(self, seg):
95+
"""
96+
BY. Party to which merchandise is sold.
97+
NAD+BY+5550534000017::9'
98+
NAD segment: ['BY', ['5550534001649', '', '9']]
99+
100+
SU. Party which manufactures or otherwise has possession of
101+
goods,and consigns or makes them available in trade.
102+
NAD+SU+<Supplier GLN>::9'
103+
"""
104+
105+
partner_dict = {}
106+
codes = ["BY", "SU"]
107+
reference_code = seg[0]
108+
if reference_code not in codes:
109+
raise NotImplementedError(f"Code '{reference_code}' not implemented")
110+
#
111+
party_identification = seg[1]
112+
party_id = party_identification[0]
113+
agency_code = party_identification[2]
114+
nameref = self.MAP_AGENCY_CODE_2_RES_PARTNER_NAMEREF.get(agency_code, "gln")
115+
partner_dict[nameref] = party_id
116+
return partner_dict
117+
118+
@api.model
119+
def map2odoo_address(self, seg):
120+
"""
121+
DP. Party to which goods should be delivered, if not identical with
122+
consignee.
123+
NAD+DP+5550534000086::9+++++++DE'
124+
NAD segment: ['DP', ['5550534022101', '', '9'], '', '', '', '', '', '', 'ES']
125+
IV. Party to whom an invoice is issued.
126+
NAD+IV+5450534005838::9++AMAZON EU SARL:NIEDERLASSUNG
127+
DEUTSCHLAND+MARCEL-BREUER-STR. 12+MUENCHEN++80807+DE
128+
129+
:returns: {
130+
'type':
131+
'partner': {'gln':''}
132+
'address': {...}
133+
}
134+
"""
135+
if seg[0] not in ("DP", "IV"):
136+
return False
137+
order_type = "delivery" if seg[0] == "DP" else "invoice"
138+
address = dict(type=order_type, partner={}, address={})
139+
# PARTY IDENTIFICATION DETAILS
140+
iden = seg[1]
141+
party_id = iden[0]
142+
agency_code = iden[2]
143+
nameref = self.MAP_AGENCY_CODE_2_RES_PARTNER_NAMEREF.get(agency_code, "gln")
144+
address["partner"][nameref] = party_id
145+
d = address["address"]
146+
# PARTY NAME
147+
if bool(seg[2]):
148+
d["name"] = seg[2]
149+
if bool(seg[3]):
150+
d["name"] = "{}{}".format(f"{d['name']}. " if d.get("name") else "", seg[3])
151+
if bool(seg[4]):
152+
# Street address and/or PO Box number in a structured address: one to three lines.
153+
d["street"] = seg[4]
154+
if bool(seg[5]):
155+
d["city"] = seg[5]
156+
if bool(seg[6]):
157+
# Country sub-entity identification
158+
d["state_code"] = seg[6]
159+
if bool(seg[7]):
160+
d["zip"] = seg[7]
161+
if bool(seg[8]):
162+
# Country, coded ISO 3166
163+
d["country_code"] = seg[8]
164+
165+
return address
166+
167+
@api.model
168+
def map2odoo_currency(self, seg):
169+
"""
170+
['2', 'EUR', '9']
171+
"""
172+
# Identification of the name or symbol of the monetary unit involved in the transaction.
173+
currency_coded = seg[1]
174+
return {
175+
"iso": currency_coded,
176+
"symbol": self.CURRENCY_SYMBOLS.get(currency_coded, False),
177+
}
178+
179+
@api.model
180+
def map2odoo_product(self, seg):
181+
"""
182+
:seg: LIN segment
183+
['1', '', ['8885583503464', 'EN']]
184+
EN. International Article Numbering Association (EAN)
185+
UP. UPC (Universal product code)
186+
SRV. GTIN
187+
"""
188+
product = seg[2]
189+
pct = product[1]
190+
return dict(code=product[0]) if pct == "SRV" else dict(barcode=product[0])
191+
192+
@api.model
193+
def map2odoo_qty(self, seg):
194+
"""
195+
'QTY' EDI segment: [['21', '2']]
196+
'21'. Ordered quantity
197+
"""
198+
return float(seg[0][1])
199+
200+
@api.model
201+
def map2odoo_unit_price(self, seg):
202+
"""
203+
'PRI' EDI segment: [['AAA', '19.75']]
204+
Price qualifier:
205+
* 'AAA'. Calculation net
206+
* 'AAB'. Calculation gross
207+
"""
208+
pri = seg[0]
209+
if pri[0] == "AAA":
210+
return float(pri[1])
211+
return 0.0

base_edifact/readme/CONFIGURE.rst

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Requires partner_identification_gln module to store GLN identifiers.
2+
3+
Partner identification code assigned by the European Article Numbering Association.
4+
5+
Use two identification categories:
6+
7+
- "GLN Identificatin Number". Partner identification like invoice or delivery address.
8+
- "GCP Identification Number". Global Company Prefix.
9+
10+
If GCP codes are needed in UNB interchange header.
11+
12+
If you need group partners, consider oca/partner-contact/partner_company_group module.

base_edifact/readme/CONTRIBUTORS.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* Rafa Morant <rmorant@albasoft.com> (www.albasoft.com)
2+
* Marc Poch <mpoch@planetatic.com>

base_edifact/readme/DESCRIPTION.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
This module contains methods to generate and parse EDIFACT/D96A files

base_edifact/readme/HISTORY.rst

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
14.0.1.0.0 (2023-04-13)
3+
~~~~~~~~~~~~~~~~~~~~~~~
4+
Strong migration from 12.0.1.0.1 because it's not working for Amazon vendor orders.

0 commit comments

Comments
 (0)