|
|
|
|
@@ -8,13 +8,13 @@ import json
|
|
|
|
|
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 @@ class ImportSupplierInvoice(Document):
|
|
|
|
|
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 @@ class ImportSupplierInvoice(Document):
|
|
|
|
|
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 @@ class ImportSupplierInvoice(Document):
|
|
|
|
|
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 @@ def get_payment_terms_from_file(file_content):
|
|
|
|
|
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 @@ def create_supplier(**args):
|
|
|
|
|
["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 @@ def create_supplier(**args):
|
|
|
|
|
|
|
|
|
|
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 @@ def create_supplier(**args):
|
|
|
|
|
|
|
|
|
|
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 @@ def create_address(**args):
|
|
|
|
|
|
|
|
|
|
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 @@ def create_address(**args):
|
|
|
|
|
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 @@ def create_purchase_invoice(**args):
|
|
|
|
|
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):
|
|
|
|
|
|