mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-20 15:32:14 +00:00
* fix: credit note creation during pos invoice consolidation (#46277)
* fix: credit note creation during pos invoice consolidation
* fix: added check to skip merging empty list of return pos invoices
* fix: sql query
* fix: using return invoice name instead of return invoice object
* fix: added pos invoice field in sales invoice item
(cherry picked from commit 8ba4ac3b86)
# Conflicts:
# erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
* chore: resolve conflict
---------
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
This commit is contained in:
@@ -8,6 +8,7 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import map_child_doc, map_doc
|
from frappe.model.mapper import map_child_doc, map_doc
|
||||||
|
from frappe.query_builder import DocType
|
||||||
from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
|
from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
|
||||||
from frappe.utils.background_jobs import enqueue, is_job_enqueued
|
from frappe.utils.background_jobs import enqueue, is_job_enqueued
|
||||||
from frappe.utils.scheduler import is_scheduler_inactive
|
from frappe.utils.scheduler import is_scheduler_inactive
|
||||||
@@ -118,17 +119,18 @@ class POSInvoiceMergeLog(Document):
|
|||||||
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
|
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
|
||||||
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
|
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
|
||||||
|
|
||||||
sales_invoice, credit_note = "", ""
|
sales_invoice, credit_notes = "", {}
|
||||||
sales_invoice_doc = None
|
sales_invoice_doc = None
|
||||||
if sales:
|
if sales:
|
||||||
sales_invoice_doc = self.process_merging_into_sales_invoice(sales)
|
sales_invoice_doc = self.process_merging_into_sales_invoice(sales)
|
||||||
sales_invoice = sales_invoice_doc.name
|
sales_invoice = sales_invoice_doc.name
|
||||||
|
|
||||||
if returns:
|
if returns:
|
||||||
credit_note = self.process_merging_into_credit_note(returns, sales_invoice_doc)
|
distinguished_returns = self.distinguish_return_pos_invoices(returns, sales_invoice_doc)
|
||||||
|
credit_notes = self.process_merging_into_credit_notes(distinguished_returns)
|
||||||
|
|
||||||
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
||||||
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
|
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_notes)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
||||||
@@ -158,34 +160,50 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
return sales_invoice
|
return sales_invoice
|
||||||
|
|
||||||
def process_merging_into_credit_note(self, data, sales_invoice_doc=None):
|
def process_merging_into_credit_notes(self, data):
|
||||||
credit_note = self.get_new_sales_invoice()
|
credit_notes = {}
|
||||||
credit_note.is_return = 1
|
for key, value in data.items():
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
|
||||||
credit_note = self.merge_pos_invoice_into(credit_note, data)
|
credit_note = self.get_new_sales_invoice()
|
||||||
referenes = {}
|
credit_note.is_return = 1
|
||||||
|
|
||||||
if sales_invoice_doc:
|
credit_note = self.merge_pos_invoice_into(credit_note, value)
|
||||||
credit_note.return_against = sales_invoice_doc.name
|
credit_note.return_against = key
|
||||||
|
|
||||||
for d in sales_invoice_doc.items:
|
credit_note.is_consolidated = 1
|
||||||
referenes[d.item_code] = d.name
|
credit_note.set_posting_time = 1
|
||||||
|
credit_note.posting_date = getdate(self.posting_date)
|
||||||
|
credit_note.posting_time = get_time(self.posting_time)
|
||||||
|
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
||||||
|
# credit_note.return_against = self.consolidated_invoice
|
||||||
|
credit_note.save()
|
||||||
|
credit_note.submit()
|
||||||
|
|
||||||
for d in credit_note.items:
|
self.consolidated_credit_note = credit_note.name
|
||||||
d.sales_invoice_item = referenes.get(d.item_code)
|
credit_notes[credit_note.name] = [d.name for d in value]
|
||||||
|
|
||||||
credit_note.is_consolidated = 1
|
return credit_notes
|
||||||
credit_note.set_posting_time = 1
|
|
||||||
credit_note.posting_date = getdate(self.posting_date)
|
|
||||||
credit_note.posting_time = get_time(self.posting_time)
|
|
||||||
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
|
||||||
# credit_note.return_against = self.consolidated_invoice
|
|
||||||
credit_note.save()
|
|
||||||
credit_note.submit()
|
|
||||||
|
|
||||||
self.consolidated_credit_note = credit_note.name
|
def distinguish_return_pos_invoices(self, data, sales_invoice_doc=None):
|
||||||
|
return_invoices = {}
|
||||||
|
|
||||||
return credit_note.name
|
return_invoices[sales_invoice_doc.name if sales_invoice_doc else None] = []
|
||||||
|
|
||||||
|
for doc in data:
|
||||||
|
sales_invoices_of_return_against = frappe.db.get_value(
|
||||||
|
"POS Invoice", doc.return_against, "consolidated_invoice"
|
||||||
|
)
|
||||||
|
if sales_invoices_of_return_against:
|
||||||
|
if sales_invoices_of_return_against in return_invoices:
|
||||||
|
return_invoices[sales_invoices_of_return_against].append(doc)
|
||||||
|
else:
|
||||||
|
return_invoices[sales_invoices_of_return_against] = [doc]
|
||||||
|
else:
|
||||||
|
return_invoices[sales_invoice_doc.name if sales_invoice_doc else None].append(doc)
|
||||||
|
|
||||||
|
return return_invoices
|
||||||
|
|
||||||
def merge_pos_invoice_into(self, invoice, data):
|
def merge_pos_invoice_into(self, invoice, data):
|
||||||
items, payments, taxes = [], [], []
|
items, payments, taxes = [], [], []
|
||||||
@@ -211,33 +229,20 @@ class POSInvoiceMergeLog(Document):
|
|||||||
loyalty_amount_sum += doc.loyalty_amount
|
loyalty_amount_sum += doc.loyalty_amount
|
||||||
|
|
||||||
for item in doc.get("items"):
|
for item in doc.get("items"):
|
||||||
found = False
|
item.rate = item.net_rate
|
||||||
for i in items:
|
item.amount = item.net_amount
|
||||||
if (
|
item.base_amount = item.base_net_amount
|
||||||
i.item_code == item.item_code
|
item.price_list_rate = 0
|
||||||
and not i.serial_and_batch_bundle
|
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
|
||||||
and not i.serial_no
|
si_item.pos_invoice = doc.name
|
||||||
and not i.batch_no
|
si_item.pos_invoice_item = item.name
|
||||||
and i.uom == item.uom
|
if doc.is_return:
|
||||||
and i.net_rate == item.net_rate
|
si_item.sales_invoice_item = get_sales_invoice_item(
|
||||||
and i.warehouse == item.warehouse
|
doc.return_against, item.pos_invoice_item
|
||||||
):
|
)
|
||||||
found = True
|
if item.serial_and_batch_bundle:
|
||||||
i.qty = i.qty + item.qty
|
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
|
||||||
i.amount = i.amount + item.net_amount
|
items.append(si_item)
|
||||||
i.net_amount = i.amount
|
|
||||||
i.base_amount = i.base_amount + item.base_net_amount
|
|
||||||
i.base_net_amount = i.base_amount
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
item.rate = item.net_rate
|
|
||||||
item.amount = item.net_amount
|
|
||||||
item.base_amount = item.base_net_amount
|
|
||||||
item.price_list_rate = 0
|
|
||||||
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
|
|
||||||
if item.serial_and_batch_bundle:
|
|
||||||
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
|
|
||||||
items.append(si_item)
|
|
||||||
|
|
||||||
for tax in doc.get("taxes"):
|
for tax in doc.get("taxes"):
|
||||||
found = False
|
found = False
|
||||||
@@ -327,16 +332,16 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
return sales_invoice
|
return sales_invoice
|
||||||
|
|
||||||
def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_note=""):
|
def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_notes=None):
|
||||||
for doc in invoice_docs:
|
for doc in invoice_docs:
|
||||||
doc.load_from_db()
|
doc.load_from_db()
|
||||||
doc.update(
|
inv = sales_invoice
|
||||||
{
|
if doc.is_return:
|
||||||
"consolidated_invoice": None
|
for key, value in credit_notes.items():
|
||||||
if self.docstatus == 2
|
if doc.name in value:
|
||||||
else (credit_note if doc.is_return else sales_invoice)
|
inv = key
|
||||||
}
|
break
|
||||||
)
|
doc.update({"consolidated_invoice": None if self.docstatus == 2 else inv})
|
||||||
doc.set_status(update=True)
|
doc.set_status(update=True)
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
@@ -628,3 +633,26 @@ def get_error_message(message) -> str:
|
|||||||
return message["message"]
|
return message["message"]
|
||||||
except Exception:
|
except Exception:
|
||||||
return str(message)
|
return str(message)
|
||||||
|
|
||||||
|
|
||||||
|
def get_sales_invoice_item(return_against_pos_invoice, pos_invoice_item):
|
||||||
|
try:
|
||||||
|
SalesInvoice = DocType("Sales Invoice")
|
||||||
|
SalesInvoiceItem = DocType("Sales Invoice Item")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(SalesInvoice)
|
||||||
|
.from_(SalesInvoiceItem)
|
||||||
|
.select(SalesInvoiceItem.name)
|
||||||
|
.where(
|
||||||
|
(SalesInvoice.name == SalesInvoiceItem.parent)
|
||||||
|
& (SalesInvoice.is_return == 0)
|
||||||
|
& (SalesInvoiceItem.pos_invoice == return_against_pos_invoice)
|
||||||
|
& (SalesInvoiceItem.pos_invoice_item == pos_invoice_item)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = query.run(as_dict=True)
|
||||||
|
return result[0].name if result else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|||||||
@@ -105,6 +105,9 @@
|
|||||||
"delivery_note",
|
"delivery_note",
|
||||||
"dn_detail",
|
"dn_detail",
|
||||||
"delivered_qty",
|
"delivered_qty",
|
||||||
|
"column_break_vwhb",
|
||||||
|
"pos_invoice",
|
||||||
|
"pos_invoice_item",
|
||||||
"internal_transfer_section",
|
"internal_transfer_section",
|
||||||
"purchase_order",
|
"purchase_order",
|
||||||
"column_break_92",
|
"column_break_92",
|
||||||
@@ -945,19 +948,43 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "pos_invoice_item",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
|
"label": "POS Invoice Item",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_vwhb",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "pos_invoice",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "POS Invoice",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "POS Invoice",
|
||||||
|
"print_hide": 1,
|
||||||
|
"search_index": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"grid_page_length": 50,
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-11-25 16:27:33.287341",
|
"modified": "2025-03-05 12:22:11.801692",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
"naming_rule": "Random",
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"row_format": "Dynamic",
|
||||||
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ class SalesInvoiceItem(Document):
|
|||||||
parent: DF.Data
|
parent: DF.Data
|
||||||
parentfield: DF.Data
|
parentfield: DF.Data
|
||||||
parenttype: DF.Data
|
parenttype: DF.Data
|
||||||
|
pos_invoice: DF.Link | None
|
||||||
|
pos_invoice_item: DF.Data | None
|
||||||
price_list_rate: DF.Currency
|
price_list_rate: DF.Currency
|
||||||
pricing_rules: DF.SmallText | None
|
pricing_rules: DF.SmallText | None
|
||||||
project: DF.Link | None
|
project: DF.Link | None
|
||||||
|
|||||||
Reference in New Issue
Block a user