code cleanup
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 89be499..6d15d54 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -653,3 +653,4 @@
erpnext.patches.v12_0.set_against_blanket_order_in_sales_and_purchase_order
erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim
erpnext.patches.v12_0.set_lead_title_field
+erpnext.patches.v12_0.set_permission_einvoicing
diff --git a/erpnext/patches/v12_0/set_permission_einvoicing.py b/erpnext/patches/v12_0/set_permission_einvoicing.py
index c01f34a..1095c8c 100644
--- a/erpnext/patches/v12_0/set_permission_einvoicing.py
+++ b/erpnext/patches/v12_0/set_permission_einvoicing.py
@@ -1,4 +1,5 @@
import frappe
+from erpnext.regional.italy.setup import make_custom_fields
from frappe.permissions import add_permission, update_permission_property
def execute():
@@ -7,6 +8,8 @@
if not company:
return
+ make_custom_fields()
+
add_permission('Import Supplier Invoice', 'Accounts Manager', 0)
update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'write', 1)
update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'create', 1)
\ No newline at end of file
diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js
index bbc0f92..9f1a092 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js
@@ -11,9 +11,20 @@
});
},
setup: function(frm) {
- frm.set_query("tax_account", function() {
+ frm.set_query("tax_account", function(doc) {
return {
- filters: { account_type: 'Tax' }
+ filters: {
+ account_type: 'Tax',
+ company: doc.company
+ }
+ };
+ });
+
+ frm.set_query("default_buying_price_list", function(doc) {
+ return {
+ filters: {
+ currency: frappe.get_doc(":Company", doc.company).default_currency
+ }
};
});
}
diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json
index aabf9de..59e955c 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-10-15 12:33:21.845329",
"doctype": "DocType",
"editable_grid": 1,
@@ -7,9 +8,11 @@
"invoice_series",
"company",
"item_code",
+ "column_break_5",
"supplier_group",
"tax_account",
- "column_break_5",
+ "default_buying_price_list",
+ "upload_xml_invoices_section",
"zip_file",
"import_invoices",
"status"
@@ -75,9 +78,22 @@
"label": "Invoice Series",
"options": "ACC-PINV-.YYYY.-",
"reqd": 1
+ },
+ {
+ "fieldname": "default_buying_price_list",
+ "fieldtype": "Link",
+ "label": "Default Buying Price List",
+ "options": "Price List",
+ "reqd": 1
+ },
+ {
+ "fieldname": "upload_xml_invoices_section",
+ "fieldtype": "Section Break",
+ "label": "Upload XML Invoices"
}
],
- "modified": "2019-10-19 00:15:11.404733",
+ "links": [],
+ "modified": "2019-12-10 16:37:26.793398",
"modified_by": "Administrator",
"module": "Regional",
"name": "Import Supplier Invoice",
diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
index 15e6344..1d21d39 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
@@ -8,13 +8,13 @@
import re
import traceback
import zipfile
-import frappe
+import frappe, erpnext
from frappe import _
from frappe.model.document import Document
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.utils.data import format_datetime
from bs4 import BeautifulSoup as bs
-from frappe.utils import cint, flt, today, nowdate, add_days, get_files_path
+from frappe.utils import cint, flt, today, nowdate, add_days, get_files_path, get_datetime_str
import dateutil
from frappe.utils.file_manager import save_file
@@ -28,61 +28,20 @@
self.name = "Import Invoice on " + format_datetime(self.creation)
def import_xml_data(self):
- pi_count = 0
import_file = frappe.get_doc("File", {"file_url": self.zip_file})
self.publish("File Import", _("Processing XML Files"), 1, 3)
+ self.file_count = 0
+ self.purchase_invoices_count = 0
+ self.default_uom = frappe.db.get_value("Stock Settings", fieldname="stock_uom")
+
with zipfile.ZipFile(get_full_path(self.zip_file)) as zf:
- file_count = 0
for file_name in zf.namelist():
- items = []
- taxes = []
- terms = []
- encoded_content = zf.read(file_name)
-
- try:
- content = encoded_content.decode("utf-8-sig")
- except UnicodeDecodeError:
- try:
- content = encoded_content.decode("utf-16")
- except UnicodeDecodeError as e:
- frappe.log_error(message=e, title="UTF-16 encoding error for File Name: " + file_name)
-
+ content = get_file_content(file_name, zf)
file_content = bs(content, "xml")
+ self.prepare_data_for_import(file_content, file_name, content)
- for line in file_content.find_all("DatiGeneraliDocumento"):
- document_type = line.TipoDocumento.text
- bill_date = dateutil.parser.parse(line.Data.text).strftime("%Y-%m-%d")
- invoice_no = line.Numero.text
- if len(invoice_no) != 0:
- total_discount = 0
-
- supp_dict = get_supplier_info_from_file(file_content)
- destination_code = get_destination_code_from_file(file_content)
- items, return_invoice, total_discount = get_item_from_file(file_content, self.item_code)
- taxes = get_taxes_from_file(file_content, self.tax_account)
- terms = get_payment_terms_from_file(file_content)
-
- supplier_name = create_supplier(supplier = supp_dict.get('supplier'), supplier_group = self.supplier_group,
- tax_id = supp_dict.get('tax_id'), fiscal_code = supp_dict.get('fiscal_code'),
- fiscal_regime = supp_dict.get('fiscal_regime'))
-
- address = create_address(supplier_name = supplier_name, address_line1 = supp_dict.get('address_line1'),
- city = supp_dict.get('city'), province = supp_dict.get('province'),
- pin_code = supp_dict.get('pin_code'), country = supp_dict.get('country'))
-
- pi_name = create_purchase_invoice(company = self.company, naming_series = self.invoice_series,
- supplier_name = supplier_name, bill_no = invoice_no,document_type = document_type,
- bill_date = bill_date,is_return = return_invoice, destination_code = destination_code,
- total_discount = total_discount, items = items,taxes = taxes, terms = terms,
- file_name = file_name)
-
- file_count += 1
- if pi_name:
- pi_count += 1
- file_save = save_file(file_name, encoded_content, "Purchase Invoice", pi_name, folder=None, decode=False, is_private=0, df=None)
-
- if pi_count == file_count:
+ if self.purchase_invoices_count == self.file_count:
self.status = "File Import Completed"
self.publish("File Import", _("XML Files Processed"), 2, 3)
else:
@@ -92,6 +51,78 @@
self.save()
self.publish("File Import", _("XML Files Processed"), 3, 3)
+ def prepare_data_for_import(self, file_content, file_name, encoded_content):
+ for line in file_content.find_all("DatiGeneraliDocumento"):
+ invoices_args = {
+ "company": self.company,
+ "naming_series": self.invoice_series,
+ "document_type": line.TipoDocumento.text,
+ "bill_date": get_datetime_str(line.Data.text),
+ "invoice_no": line.Numero.text,
+ "total_discount": 0,
+ "items": [],
+ "buying_price_list": self.default_buying_price_list
+ }
+
+ if not invoices_args.get("invoice_no", ''): return
+
+ supp_dict = get_supplier_details(file_content)
+ invoices_args["destination_code"] = get_destination_code_from_file(file_content)
+ invoices_args["taxes"] = get_taxes_from_file(file_content, self.tax_account)
+ invoices_args["terms"] = get_payment_terms_from_file(file_content)
+ self.prepare_items_for_invoice(file_content, invoices_args)
+
+ supplier_name = create_supplier(self.supplier_group, supp_dict)
+ address = create_address(supplier_name, supp_dict)
+ pi_name = create_purchase_invoice(supplier_name, file_name, invoices_args)
+
+ self.file_count += 1
+ if pi_name:
+ self.purchase_invoices_count += 1
+ file_save = save_file(file_name, encoded_content, "Purchase Invoice",
+ pi_name, folder=None, decode=False, is_private=0, df=None)
+
+ def prepare_items_for_invoice(self, file_content, invoices_args):
+ qty = 1
+ rate, tax_rate = [0 ,0]
+ uom = self.default_uom
+
+ #read file for item information
+ for line in file_content.find_all("DettaglioLinee"):
+ if line.find("PrezzoUnitario") and line.find("PrezzoTotale"):
+ rate = flt(line.PrezzoUnitario.text) or 0
+ line_total = flt(line.PrezzoTotale.text) or 0
+
+ if rate and flt(line_total) / rate != 1.0 and line.find("Quantita"):
+ qty = flt(line.Quantita.text) or 0
+ if line.find("UnitaMisura"):
+ uom = create_uom(line.UnitaMisura.text)
+
+ if (rate < 0 and line_total < 0):
+ qty *= -1
+ invoices_args["return_invoice"] = 1
+
+ if line.find("AliquotaIVA"):
+ tax_rate = flt(line.AliquotaIVA.text)
+
+ line_str = re.sub('[^A-Za-z0-9]+', '-', line.Descrizione.text)
+ item_name = line_str[0:140]
+
+ invoices_args['items'].append({
+ "item_code": self.item_code,
+ "item_name": item_name,
+ "description": line_str,
+ "qty": qty,
+ "uom": uom,
+ "rate": abs(rate),
+ "conversion_factor": 1.0,
+ "tax_rate": tax_rate
+ })
+
+ for disc_line in line.find_all("ScontoMaggiorazione"):
+ if disc_line.find("Percentuale"):
+ invoices_args["total_discount"] += flt((flt(disc_line.Percentuale.text) / 100) * (rate * qty))
+
def process_file_data(self):
self.status = "Processing File Data"
self.save()
@@ -100,100 +131,47 @@
def publish(self, title, message, count, total):
frappe.publish_realtime("import_invoice_update", {"title": title, "message": message, "count": count, "total": total})
-def get_supplier_info_from_file(file_content):
+def get_file_content(file_name, zip_file_object):
+ content = ''
+ encoded_content = zip_file_object.read(file_name)
+
+ try:
+ content = encoded_content.decode("utf-8-sig")
+ except UnicodeDecodeError:
+ try:
+ content = encoded_content.decode("utf-16")
+ except UnicodeDecodeError as e:
+ frappe.log_error(message=e, title="UTF-16 encoding error for File Name: " + file_name)
+
+ return content
+
+def get_supplier_details(file_content):
supplier_info = {}
for line in file_content.find_all("CedentePrestatore"):
- tax_id = line.DatiAnagrafici.IdPaese.text + line.DatiAnagrafici.IdCodice.text
+ supplier_info['tax_id'] = line.DatiAnagrafici.IdPaese.text + line.DatiAnagrafici.IdCodice.text
if line.find("CodiceFiscale"):
- fiscal_code = line.DatiAnagrafici.CodiceFiscale.text
- else:
- fiscal_code = ""
+ supplier_info['fiscal_code'] = line.DatiAnagrafici.CodiceFiscale.text
+
if line.find("RegimeFiscale"):
- fiscal_regime = line.DatiAnagrafici.RegimeFiscale.text
- else:
- fiscal_regime = ""
+ supplier_info['fiscal_regime'] = line.DatiAnagrafici.RegimeFiscale.text
+
if line.find("Denominazione"):
- supplier = line.DatiAnagrafici.Anagrafica.Denominazione.text
+ supplier_info['supplier'] = line.DatiAnagrafici.Anagrafica.Denominazione.text
+
if line.find("Nome"):
- supplier = line.DatiAnagrafici.Anagrafica.Nome.text + " " + line.DatiAnagrafici.Anagrafica.Cognome.text
- address_line1 = line.Sede.Indirizzo.text
- city = line.Sede.Comune.text
+ supplier_info['supplier'] = (line.DatiAnagrafici.Anagrafica.Nome.text
+ + " " + line.DatiAnagrafici.Anagrafica.Cognome.text)
+
+ supplier_info['address_line1'] = line.Sede.Indirizzo.text
+ supplier_info['city'] = line.Sede.Comune.text
if line.find("Provincia"):
- province = line.Sede.Provincia.text
- else:
- province = ""
- pin_code = line.Sede.CAP.text
- country = get_country(line.Sede.Nazione.text)
- #set the dict values
- supplier_info['tax_id'] = tax_id
- supplier_info['fiscal_code'] = fiscal_code
- supplier_info['fiscal_regime'] = fiscal_regime
- supplier_info['supplier'] = supplier
- supplier_info['address_line1'] = address_line1
- supplier_info['city'] = city
- supplier_info['province'] = province
- supplier_info['pin_code'] = pin_code
- supplier_info['country'] = country
+ supplier_info['province'] = line.Sede.Provincia.text
+
+ supplier_info['pin_code'] = line.Sede.CAP.text
+ supplier_info['country'] = get_country(line.Sede.Nazione.text)
return supplier_info
-def get_item_from_file(file_content, item_code):
- items = []
- total_discount = 0
- default_uom = frappe.db.get_value("Stock Settings", fieldname="stock_uom")
- #read file for item information
- for line in file_content.find_all("DettaglioLinee"):
- if line.find("PrezzoUnitario") and line.find("PrezzoTotale"):
- unit_rate = flt(line.PrezzoUnitario.text) or 0
- line_total = flt(line.PrezzoTotale.text) or 0
-
- if (unit_rate == 0.0):
- qty = 1.0
- uom = default_uom
- rate = tax_rate = 0
- else:
- if (line_total / unit_rate) == 1.0:
- qty = 1.0
- uom = default_uom
- else:
- if line.find("Quantita"):
- qty = flt(line.Quantita.text) or 0
- if line.find("UnitaMisura"):
- uom = create_uom(line.UnitaMisura.text)
- else:
- uom = default_uom
-
- if (unit_rate < 0 and line_total < 0):
- qty *= -1
- return_invoice = 1
- unit_rate *= -1
- else:
- return_invoice = 0
-
- rate = unit_rate
- if line.find("AliquotaIVA"):
- tax_rate = flt(line.AliquotaIVA.text)
-
- line_str = re.sub('[^A-Za-z0-9]+', '-', line.Descrizione.text)
- item_name = line_str[0:140]
- items.append({
- "item_code": item_code,
- "item_name": item_name,
- "description": line_str,
- "qty": qty,
- "uom": uom,
- "rate": rate,
- "conversion_factor": 1.0,
- "tax_rate": tax_rate
- })
-
- for disc_line in line.find_all("ScontoMaggiorazione"):
- if disc_line.find("Percentuale"):
- discount = flt(disc_line.Percentuale.text) or 0
- total_discount += flt((discount / 100) * (rate * qty))
-
- return items, return_invoice, total_discount
-
def get_taxes_from_file(file_content, tax_account):
taxes = []
#read file for taxes information
@@ -227,22 +205,24 @@
else:
due_date = today()
terms.append({
- "mode_of_payment_code": mop_code,
- "bank_account_iban": line.IBAN.text if line.find("IBAN") else "",
- "due_date": due_date,
- "payment_amount": line.ImportoPagamento.text
+ "mode_of_payment_code": mop_code,
+ "bank_account_iban": line.IBAN.text if line.find("IBAN") else "",
+ "due_date": due_date,
+ "payment_amount": line.ImportoPagamento.text
})
return terms
def get_destination_code_from_file(file_content):
+ destination_code = ''
for line in file_content.find_all("DatiTrasmissione"):
destination_code = line.CodiceDestinatario.text
return destination_code
-def create_supplier(**args):
+def create_supplier(supplier_group, args):
args = frappe._dict(args)
+
existing_supplier_name = frappe.db.get_value("Supplier",
filters={"tax_id": args.tax_id}, fieldname="name")
if existing_supplier_name:
@@ -258,11 +238,7 @@
["Dynamic Link", "parenttype", "=", "Contact"]
]
- existing_contacts = frappe.get_list("Contact", filters)
-
- if existing_contacts:
- pass
- else:
+ if not frappe.get_list("Contact", filters):
new_contact = frappe.new_doc("Contact")
new_contact.first_name = args.supplier
new_contact.append('links', {
@@ -276,7 +252,7 @@
new_supplier = frappe.new_doc("Supplier")
new_supplier.supplier_name = args.supplier
- new_supplier.supplier_group = args.supplier_group
+ new_supplier.supplier_group = supplier_group
new_supplier.tax_id = args.tax_id
new_supplier.fiscal_code = args.fiscal_code
new_supplier.fiscal_regime = args.fiscal_regime
@@ -293,11 +269,12 @@
return new_supplier.name
-def create_address(**args):
+def create_address(supplier_name, args):
args = frappe._dict(args)
+
filters = [
["Dynamic Link", "link_doctype", "=", "Supplier"],
- ["Dynamic Link", "link_name", "=", args.supplier_name],
+ ["Dynamic Link", "link_name", "=", supplier_name],
["Dynamic Link", "parenttype", "=", "Address"]
]
@@ -324,7 +301,7 @@
new_address_doc.append("links", {
"link_doctype": "Supplier",
- "link_name": args.supplier_name
+ "link_name": supplier_name
})
new_address_doc.address_type = "Billing"
new_address_doc.insert(ignore_mandatory=True)
@@ -332,26 +309,29 @@
else:
return None
-def create_purchase_invoice(**args):
+def create_purchase_invoice(supplier_name, file_name, args):
args = frappe._dict(args)
pi = frappe.get_doc({
- "doctype": "Purchase Invoice",
- "company": args.company,
- "naming_series": args.naming_series,
- "supplier": args.supplier_name,
- "is_return": args.is_return,
- "posting_date": today(),
- "bill_no": args.bill_no,
- "bill_date": args.bill_date,
- "destination_code": args.destination_code,
- "document_type": args.document_type,
- "items": args["items"],
- "taxes": args["taxes"]
- })
+ "doctype": "Purchase Invoice",
+ "company": args.company,
+ "currency": erpnext.get_company_currency(args.company),
+ "naming_series": args.naming_series,
+ "supplier": supplier_name,
+ "is_return": args.is_return,
+ "posting_date": today(),
+ "bill_no": args.bill_no,
+ "buying_price_list": args.buying_price_list,
+ "bill_date": args.bill_date,
+ "destination_code": args.destination_code,
+ "document_type": args.document_type,
+ "items": args["items"],
+ "taxes": args["taxes"]
+ })
try:
pi.set_missing_values()
pi.insert(ignore_mandatory=True)
+
#if discount exists in file, apply any discount on grand total
if args.total_discount > 0:
pi.apply_discount_on = "Grand Total"
@@ -375,7 +355,8 @@
pi.save()
return pi.name
except Exception as e:
- frappe.log_error(message=e, title="Create Purchase Invoice: " + args.bill_no + "File Name: " + args.file_name)
+ frappe.log_error(message=e,
+ title="Create Purchase Invoice: " + args.bill_no + "File Name: " + file_name)
return None
def get_country(code):