diff --git a/erpnext/buying/doctype/supplier/regional/france.js b/erpnext/buying/doctype/supplier/regional/france.js index 2ffa764d0b29da7fb9935656a64a130ddf4277d1..72aaf6f6a4d94ed20cdde26ec8c2ffcdd75e23bb 100644 --- a/erpnext/buying/doctype/supplier/regional/france.js +++ b/erpnext/buying/doctype/supplier/regional/france.js @@ -1,6 +1,6 @@ frappe.ui.form.on("Supplier", { - setup: function (frm) { - frm.set_query("company_search", function(doc) { + setup(frm) { + frm.set_query("company_search", function (doc) { return { query: "erpnext.regional.france.extensions.supplier.company_query" }; @@ -11,10 +11,11 @@ frappe.ui.form.on("Supplier", { if (frm.get_field("company_search")._data.length) { const selection = frm.get_field("company_search")._data.filter(f => f.value == frm.doc.company_search) - if (selection.length){ - const selected_company = selection[0] - frm.set_value("supplier_name", selected_company.label) - frm.set_value("siren_number", selected_company.value) + if (selection.length) { + const selected_company = selection[0]; + frm.set_value("supplier_name", selected_company.label); + frm.set_value("siren_number", selected_company.value.substring(4)); + frm.set_value("tax_id", selected_company.value); } } } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2a93c3eb9ced3d8092609e88ef97e9837a66fbde..a4867df436667c902ebfdb0f4ecddf2e7852332e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -387,6 +387,7 @@ erpnext.accounts.doctype.mode_of_payment.patches.migrate_fees_and_cost_center_to execute:frappe.delete_doc_if_exists("DocType", "Integration References") execute:frappe.delete_doc_if_exists("DocType", "Social Media Post") erpnext.patches.dokos.v4_0.update_advance_for_down_payments #2024-05-207 +erpnext.patches.dokos.v4_0.check_enable_pappers # @dokos diff --git a/erpnext/patches/dokos/v4_0/check_enable_pappers.py b/erpnext/patches/dokos/v4_0/check_enable_pappers.py new file mode 100644 index 0000000000000000000000000000000000000000..e8cb4fa60de460b8ca6c91535dc7978153a27bca --- /dev/null +++ b/erpnext/patches/dokos/v4_0/check_enable_pappers.py @@ -0,0 +1,7 @@ +import frappe + + +def execute(): + global_defauts = frappe.get_single("Global Defaults") + if global_defauts.pappers_api_key: + frappe.db.set_single_value("Global Defaults", "enable_pappers", 1) diff --git a/erpnext/public/js/utils/contact_address_quick_entry.js b/erpnext/public/js/utils/contact_address_quick_entry.js index 6b5254c84c303e23e94b83a719db5609daaadf7c..54dd4155020a11e51e4a7dd1c4db0b4e82827583 100644 --- a/erpnext/public/js/utils/contact_address_quick_entry.js +++ b/erpnext/public/js/utils/contact_address_quick_entry.js @@ -35,8 +35,10 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm const selected_company = selection[0] me.dialog.set_values({ customer_name: selected_company.label, - siren_number: selected_company.value - }) + supplier_name: selected_company.label, + tax_id: selected_company.value, + }); + me.dialog.refresh(); } } } diff --git a/erpnext/regional/france/extensions/customer.py b/erpnext/regional/france/extensions/customer.py index 56fe6b5677d164eeafb84ed78ce7e4bcba90c20b..336d39c1e87840e413711f1c74e35f72ce6a363b 100644 --- a/erpnext/regional/france/extensions/customer.py +++ b/erpnext/regional/france/extensions/customer.py @@ -1,9 +1,7 @@ from erpnext.regional.france.extensions.supplier import ( - get_info_from_pappers, - get_siren_from_tax_id, + get_pappers_data, ) def validate(doc, method): - get_siren_from_tax_id(doc) - get_info_from_pappers(doc) + get_pappers_data(doc) diff --git a/erpnext/regional/france/extensions/supplier.py b/erpnext/regional/france/extensions/supplier.py index fc07a925cdba0ac07b13f5afb019a5ee8290e506..b2cb46441aec8a854c51d7fab70367573f802d76 100644 --- a/erpnext/regional/france/extensions/supplier.py +++ b/erpnext/regional/france/extensions/supplier.py @@ -1,5 +1,6 @@ import frappe from frappe import _ +from frappe.contacts.doctype.address.address import get_default_address from frappe.utils import cint, date_diff, nowdate from erpnext.regional.france.pappers.entreprise import PappersEntreprise @@ -7,29 +8,32 @@ from erpnext.regional.france.pappers.recherche import PappersRecherche def validate(doc, method): + get_pappers_data(doc) + +def get_pappers_data(doc): meta = frappe.get_meta(doc.doctype) if meta.has_field("siren_number"): - get_siren_from_tax_id(doc) + if doc.tax_id and not doc.get("siren_number"): + get_siren_from_tax_id(doc.tax_id) get_info_from_pappers(doc) -def get_siren_from_tax_id(doc): - if doc.tax_id and not doc.get("siren_number"): - doc.siren_number = doc.tax_id[4:] +def get_siren_from_tax_id(tax_id): + return tax_id[4:] def get_info_from_pappers(doc): global_defauts = frappe.get_single("Global Defaults") - if not global_defauts.pappers_api_key: + if not global_defauts.enable_pappers or not global_defauts.pappers_api_key: return - if not doc.siren_number: + if not doc.siren_number or not (doc.tax_id and doc.tax_id.startswith("FR")): return if date_diff(nowdate(), doc.last_pappers_update) > cint(global_defauts.pappers_update_interval): return - data = PappersEntreprise().get({"siren": doc.siren_number}) + data = PappersEntreprise().get({"siren": doc.siren_number or get_siren_from_tax_id(doc.tax_id)}) if data and not data.get("statusCode"): update_tax_id(doc, data.get("numero_tva_intracommunautaire")) @@ -39,6 +43,8 @@ def get_info_from_pappers(doc): if meta.has_field(key): doc.set(key, value) + update_billing_address(doc, data) + doc.last_pappers_update = nowdate() @@ -49,16 +55,42 @@ def update_tax_id(doc, vat_number): elif doc.tax_id != vat_number: frappe.msgprint( _( - "The VAT number registered in this document doesn't match the VAT number available publicly for this company: {}".format( + "The VAT number registered in this document doesn't match the VAT number available publicly for this company: {}".format( # noqa: UP032 vat_number ) ) ) +def update_billing_address(doc, pappers_data): + data = frappe._dict(pappers_data.get("siege", {})) + if not get_default_address(doc.doctype, doc.name, sort_key="is_primary_address"): + if data.get("ville") and frappe.db.exists("Country", data.get("pays")): + address = frappe.new_doc("Address") + address.update( + { + "address_title": pappers_data.get("denomination"), + "address_type": "Billing", + "address_line1": data.get("adresse_ligne_1"), + "address_line2": data.get("adresse_ligne_2"), + "city": data.get("ville"), + "country": data.get("pays"), + } + ) + address.append("links", {"link_doctype": doc.doctype, "link_name": doc.name}) + address.insert() + + @frappe.whitelist() def company_query(txt): if txt: + if txt.startswith("FR") and len(txt) == 13 and txt[4:].isdigit(): + if res := get_company_by_siren(get_siren_from_tax_id(txt)): + return res + if len(txt.replace(" ", "")) == 9 and txt.replace(" ", "").isdigit(): + if res := get_company_by_siren(txt.replace(" ", "")): + return res + res = PappersRecherche().get( {"q": txt, "cibles": "nom_entreprise,siren,siret", "longueur": 100, "api_token": None} ) @@ -71,10 +103,26 @@ def company_query(txt): return list( { "label": r.get("nom_entreprise"), - "value": r.get("siren"), + "value": r.get("numero_tva_intracommunautaire") or calculated_vat_number(r.get('siren')), "description": f"SIREN: {r.get('siren_formate')}
{r.get('siege', {}).get('adresse_ligne_1', '')} {r.get('siege', {}).get('code_postal', '')} {r.get('siege', {}).get('ville', '')}", } for r in res.get("resultats_nom_entreprise", []) ) return [] + +def get_company_by_siren(siren): + res = PappersEntreprise().get({"siren": siren}) + if not res.get("statusCode"): + return list([ + { + "label": res.get("nom_entreprise"), + "value": res.get("numero_tva_intracommunautaire"), + "description": f"SIREN: {res.get('siren_formate')}
{res.get('siege', {}).get('adresse_ligne_1', '')} {res.get('siege', {}).get('code_postal', '')} {res.get('siege', {}).get('ville', '')}", + } + ]) + + return [] + +def calculated_vat_number(siren): + return f"FR{(12+3*int(siren)%97)%97}{siren}" diff --git a/erpnext/regional/france/pappers/api.py b/erpnext/regional/france/pappers/api.py index 51486af13c79cf92b8376a71497de358d0574d19..592d187de055fcec4d5f2c5aab27b6831d2f941c 100644 --- a/erpnext/regional/france/pappers/api.py +++ b/erpnext/regional/france/pappers/api.py @@ -27,6 +27,8 @@ def setup_pappers(doc, method): def setup_custom_fields(): + # _("The search tool uses Pappers API") + pappers_fields = [ dict( fieldname="public_information_tab", @@ -249,7 +251,7 @@ def setup_custom_fields(): label="Company Search", fieldtype="Autocomplete", insert_after="naming_series", - description="The search tool uses Pappers API.
You are limited to 100 searches per day.", + description="The search tool uses Pappers API", allow_in_quick_entry=True, ), ] diff --git a/erpnext/regional/france/pappers/entreprise.py b/erpnext/regional/france/pappers/entreprise.py index 8860e3712f7bf81441fe119fe16c1f2dbffcd99a..ed262fa64305ee682ab31a54e648b97129c0fab3 100644 --- a/erpnext/regional/france/pappers/entreprise.py +++ b/erpnext/regional/france/pappers/entreprise.py @@ -3,5 +3,5 @@ from erpnext.regional.france.pappers.api import PappersAPI class PappersEntreprise(PappersAPI): def __init__(self): - super(PappersEntreprise, self).__init__() + super().__init__() self.url = f"{self.base_url.rstrip('/')}/entreprise" diff --git a/erpnext/selling/doctype/customer/regional/france.js b/erpnext/selling/doctype/customer/regional/france.js index b1f9686c43bc8599c6e35e56910fafb9b307aa47..7e56e6dea86f8291d5f15e4c0cdd9eaaf89213b5 100644 --- a/erpnext/selling/doctype/customer/regional/france.js +++ b/erpnext/selling/doctype/customer/regional/france.js @@ -11,10 +11,11 @@ frappe.ui.form.on("Customer", { if (frm.get_field("company_search")._data.length) { const selection = frm.get_field("company_search")._data.filter(f => f.value == frm.doc.company_search) - if (selection.length){ - const selected_company = selection[0] - frm.set_value("customer_name", selected_company.label) - frm.set_value("siren_number", selected_company.value) + if (selection.length) { + const selected_company = selection[0]; + frm.set_value("customer_name", selected_company.label); + frm.set_value("siren_number", selected_company.value.substring(4)); + frm.set_value("tax_id", selected_company.value); } } } diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json index 640f666974847a77121795063bab354739a4d94a..7bd158b2cda36c40c5d18a4d7027e9401dfaeab9 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.json +++ b/erpnext/setup/doctype/global_defaults/global_defaults.json @@ -15,6 +15,8 @@ "disable_in_words", "pappers_tab", "pappers_help", + "enable_pappers", + "section_break_cdgg", "pappers_api_key", "pappers_update_interval" ], @@ -88,15 +90,27 @@ "label": "Pappers Help" }, { + "depends_on": "eval:doc.enable_pappers", "fieldname": "pappers_api_key", "fieldtype": "Password", "label": "API Key" }, { "default": "90", + "depends_on": "eval:doc.enable_pappers", "fieldname": "pappers_update_interval", "fieldtype": "Int", "label": "Update data every (Days)" + }, + { + "default": "0", + "fieldname": "enable_pappers", + "fieldtype": "Check", + "label": "Enable Pappers" + }, + { + "fieldname": "section_break_cdgg", + "fieldtype": "Section Break" } ], "icon": "fa fa-cog", @@ -104,7 +118,7 @@ "in_create": 1, "issingle": 1, "links": [], - "modified": "2023-09-02 16:42:42.943877", + "modified": "2024-12-20 21:19:40.872287", "modified_by": "Administrator", "module": "Setup", "name": "Global Defaults", @@ -122,4 +136,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} +} \ No newline at end of file diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py index 2e7fdd759e58c5565e6ccd6b58b9469f8526c0cb..8c26cf1f859abacb40c3b4fe7777ce7557eaa6c4 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.py +++ b/erpnext/setup/doctype/global_defaults/global_defaults.py @@ -37,6 +37,7 @@ class GlobalDefaults(Document): default_distance_unit: DF.Link | None disable_in_words: DF.Check disable_rounded_total: DF.Check + enable_pappers: DF.Check hide_currency_symbol: DF.Literal["", "No", "Yes"] pappers_api_key: DF.Password | None pappers_update_interval: DF.Int