mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 08:54:45 +00:00
fix: POS return for Serialized Items (#24292)
Co-authored-by: Nabin Hait <nabinhait@gmail.com> Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
This commit is contained in:
@@ -179,10 +179,18 @@ class POSInvoice(SalesInvoice):
|
|||||||
if d.get("serial_no"):
|
if d.get("serial_no"):
|
||||||
serial_nos = get_serial_nos(d.serial_no)
|
serial_nos = get_serial_nos(d.serial_no)
|
||||||
for sr in serial_nos:
|
for sr in serial_nos:
|
||||||
serial_no_exists = frappe.db.exists("POS Invoice Item", {
|
serial_no_exists = frappe.db.sql("""
|
||||||
"parent": self.return_against,
|
SELECT name
|
||||||
"serial_no": ["like", d.get("serial_no")]
|
FROM `tabPOS Invoice Item`
|
||||||
})
|
WHERE
|
||||||
|
parent = %s
|
||||||
|
and (serial_no = %s
|
||||||
|
or serial_no like %s
|
||||||
|
or serial_no like %s
|
||||||
|
or serial_no like %s
|
||||||
|
)
|
||||||
|
""", (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%'))
|
||||||
|
|
||||||
if not serial_no_exists:
|
if not serial_no_exists:
|
||||||
bold_return_against = frappe.bold(self.return_against)
|
bold_return_against = frappe.bold(self.return_against)
|
||||||
bold_serial_no = frappe.bold(sr)
|
bold_serial_no = frappe.bold(sr)
|
||||||
@@ -190,7 +198,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
|
_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
|
||||||
.format(d.idx, bold_serial_no, bold_return_against)
|
.format(d.idx, bold_serial_no, bold_return_against)
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_non_stock_items(self):
|
def validate_non_stock_items(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
|
is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
|
||||||
@@ -292,7 +300,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
if not self.get('payments') and not for_validate:
|
if not self.get('payments') and not for_validate:
|
||||||
update_multi_mode_option(self, profile)
|
update_multi_mode_option(self, profile)
|
||||||
|
|
||||||
if self.is_return and not for_validate:
|
if self.is_return and not for_validate:
|
||||||
add_return_modes(self, profile)
|
add_return_modes(self, profile)
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,65 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(pos_return.get('payments')[0].amount, -500)
|
self.assertEqual(pos_return.get('payments')[0].amount, -500)
|
||||||
self.assertEqual(pos_return.get('payments')[1].amount, -500)
|
self.assertEqual(pos_return.get('payments')[1].amount, -500)
|
||||||
|
|
||||||
|
def test_pos_return_for_serialized_item(self):
|
||||||
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
|
se = make_serialized_item(company='_Test Company',
|
||||||
|
target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
|
||||||
|
|
||||||
|
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
|
||||||
|
|
||||||
|
pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
|
||||||
|
account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
|
||||||
|
expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
|
||||||
|
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
||||||
|
|
||||||
|
pos.get("items")[0].serial_no = serial_nos[0]
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
|
||||||
|
|
||||||
|
pos.insert()
|
||||||
|
pos.submit()
|
||||||
|
|
||||||
|
pos_return = make_sales_return(pos.name)
|
||||||
|
|
||||||
|
pos_return.insert()
|
||||||
|
pos_return.submit()
|
||||||
|
self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0])
|
||||||
|
|
||||||
|
def test_partial_pos_returns(self):
|
||||||
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
|
se = make_serialized_item(company='_Test Company',
|
||||||
|
target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
|
||||||
|
|
||||||
|
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
|
||||||
|
|
||||||
|
pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
|
||||||
|
account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
|
||||||
|
expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
|
||||||
|
item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1)
|
||||||
|
|
||||||
|
pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1]
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
|
||||||
|
|
||||||
|
pos.insert()
|
||||||
|
pos.submit()
|
||||||
|
|
||||||
|
pos_return1 = make_sales_return(pos.name)
|
||||||
|
|
||||||
|
# partial return 1
|
||||||
|
pos_return1.get('items')[0].qty = -1
|
||||||
|
pos_return1.get('items')[0].serial_no = serial_nos[0]
|
||||||
|
pos_return1.insert()
|
||||||
|
pos_return1.submit()
|
||||||
|
|
||||||
|
# partial return 2
|
||||||
|
pos_return2 = make_sales_return(pos.name)
|
||||||
|
self.assertEqual(pos_return2.get('items')[0].qty, -1)
|
||||||
|
self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1])
|
||||||
|
|
||||||
def test_pos_change_amount(self):
|
def test_pos_change_amount(self):
|
||||||
pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC",
|
pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC",
|
||||||
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
|
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
|
||||||
|
|||||||
@@ -87,6 +87,7 @@
|
|||||||
"edit_references",
|
"edit_references",
|
||||||
"sales_order",
|
"sales_order",
|
||||||
"so_detail",
|
"so_detail",
|
||||||
|
"pos_invoice_item",
|
||||||
"column_break_74",
|
"column_break_74",
|
||||||
"delivery_note",
|
"delivery_note",
|
||||||
"dn_detail",
|
"dn_detail",
|
||||||
@@ -790,11 +791,20 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project"
|
"options": "Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "pos_invoice_item",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
|
"label": "POS Invoice Item",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-22 13:40:34.418346",
|
"modified": "2021-01-04 17:34:49.924531",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Invoice Item",
|
"name": "POS Invoice Item",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class POSInvoiceMergeLog(Document):
|
|||||||
for d in self.pos_invoices:
|
for d in self.pos_invoices:
|
||||||
status, docstatus, is_return, return_against = frappe.db.get_value(
|
status, docstatus, is_return, return_against = frappe.db.get_value(
|
||||||
'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
|
'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
|
||||||
|
|
||||||
bold_pos_invoice = frappe.bold(d.pos_invoice)
|
bold_pos_invoice = frappe.bold(d.pos_invoice)
|
||||||
bold_status = frappe.bold(status)
|
bold_status = frappe.bold(status)
|
||||||
if docstatus != 1:
|
if docstatus != 1:
|
||||||
@@ -58,7 +58,7 @@ class POSInvoiceMergeLog(Document):
|
|||||||
sales_invoice, credit_note = "", ""
|
sales_invoice, credit_note = "", ""
|
||||||
if sales:
|
if sales:
|
||||||
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
||||||
|
|
||||||
if returns:
|
if returns:
|
||||||
credit_note = self.process_merging_into_credit_note(returns)
|
credit_note = self.process_merging_into_credit_note(returns)
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
def process_merging_into_sales_invoice(self, data):
|
def process_merging_into_sales_invoice(self, data):
|
||||||
sales_invoice = self.get_new_sales_invoice()
|
sales_invoice = self.get_new_sales_invoice()
|
||||||
|
|
||||||
sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
|
sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
|
||||||
|
|
||||||
sales_invoice.is_consolidated = 1
|
sales_invoice.is_consolidated = 1
|
||||||
@@ -98,19 +98,19 @@ class POSInvoiceMergeLog(Document):
|
|||||||
self.consolidated_credit_note = credit_note.name
|
self.consolidated_credit_note = credit_note.name
|
||||||
|
|
||||||
return credit_note.name
|
return credit_note.name
|
||||||
|
|
||||||
def merge_pos_invoice_into(self, invoice, data):
|
def merge_pos_invoice_into(self, invoice, data):
|
||||||
items, payments, taxes = [], [], []
|
items, payments, taxes = [], [], []
|
||||||
loyalty_amount_sum, loyalty_points_sum = 0, 0
|
loyalty_amount_sum, loyalty_points_sum = 0, 0
|
||||||
for doc in data:
|
for doc in data:
|
||||||
map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
|
map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
|
||||||
|
|
||||||
if doc.redeem_loyalty_points:
|
if doc.redeem_loyalty_points:
|
||||||
invoice.loyalty_redemption_account = doc.loyalty_redemption_account
|
invoice.loyalty_redemption_account = doc.loyalty_redemption_account
|
||||||
invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
|
invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
|
||||||
loyalty_points_sum += doc.loyalty_points
|
loyalty_points_sum += doc.loyalty_points
|
||||||
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
|
found = False
|
||||||
for i in items:
|
for i in items:
|
||||||
@@ -118,12 +118,13 @@ class POSInvoiceMergeLog(Document):
|
|||||||
i.uom == item.uom and i.net_rate == item.net_rate):
|
i.uom == item.uom and i.net_rate == item.net_rate):
|
||||||
found = True
|
found = True
|
||||||
i.qty = i.qty + item.qty
|
i.qty = i.qty + item.qty
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
item.rate = item.net_rate
|
item.rate = item.net_rate
|
||||||
item.price_list_rate = 0
|
item.price_list_rate = 0
|
||||||
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
|
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
|
||||||
items.append(si_item)
|
items.append(si_item)
|
||||||
|
|
||||||
for tax in doc.get('taxes'):
|
for tax in doc.get('taxes'):
|
||||||
found = False
|
found = False
|
||||||
for t in taxes:
|
for t in taxes:
|
||||||
@@ -162,7 +163,7 @@ class POSInvoiceMergeLog(Document):
|
|||||||
invoice.ignore_pricing_rule = 1
|
invoice.ignore_pricing_rule = 1
|
||||||
|
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
def get_new_sales_invoice(self):
|
def get_new_sales_invoice(self):
|
||||||
sales_invoice = frappe.new_doc('Sales Invoice')
|
sales_invoice = frappe.new_doc('Sales Invoice')
|
||||||
sales_invoice.customer = self.customer
|
sales_invoice.customer = self.customer
|
||||||
@@ -194,7 +195,7 @@ def get_all_unconsolidated_invoices():
|
|||||||
}
|
}
|
||||||
pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
|
pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
|
||||||
fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
|
fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
|
||||||
|
|
||||||
return pos_invoices
|
return pos_invoices
|
||||||
|
|
||||||
def get_invoice_customer_map(pos_invoices):
|
def get_invoice_customer_map(pos_invoices):
|
||||||
@@ -204,7 +205,7 @@ def get_invoice_customer_map(pos_invoices):
|
|||||||
customer = invoice.get('customer')
|
customer = invoice.get('customer')
|
||||||
pos_invoice_customer_map.setdefault(customer, [])
|
pos_invoice_customer_map.setdefault(customer, [])
|
||||||
pos_invoice_customer_map[customer].append(invoice)
|
pos_invoice_customer_map[customer].append(invoice)
|
||||||
|
|
||||||
return pos_invoice_customer_map
|
return pos_invoice_customer_map
|
||||||
|
|
||||||
def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
|
def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
|
||||||
|
|||||||
@@ -204,8 +204,6 @@ def get_already_returned_items(doc):
|
|||||||
return items
|
return items
|
||||||
|
|
||||||
def get_returned_qty_map_for_row(row_name, doctype):
|
def get_returned_qty_map_for_row(row_name, doctype):
|
||||||
if doctype == "POS Invoice": return {}
|
|
||||||
|
|
||||||
child_doctype = doctype + " Item"
|
child_doctype = doctype + " Item"
|
||||||
reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
|
reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
|
||||||
|
|
||||||
@@ -354,7 +352,12 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
target_doc.so_detail = source_doc.so_detail
|
target_doc.so_detail = source_doc.so_detail
|
||||||
target_doc.dn_detail = source_doc.dn_detail
|
target_doc.dn_detail = source_doc.dn_detail
|
||||||
target_doc.expense_account = source_doc.expense_account
|
target_doc.expense_account = source_doc.expense_account
|
||||||
target_doc.sales_invoice_item = source_doc.name
|
|
||||||
|
if doctype == "Sales Invoice":
|
||||||
|
target_doc.sales_invoice_item = source_doc.name
|
||||||
|
else:
|
||||||
|
target_doc.pos_invoice_item = source_doc.name
|
||||||
|
|
||||||
target_doc.price_list_rate = 0
|
target_doc.price_list_rate = 0
|
||||||
if default_warehouse_for_sales_return:
|
if default_warehouse_for_sales_return:
|
||||||
target_doc.warehouse = default_warehouse_for_sales_return
|
target_doc.warehouse = default_warehouse_for_sales_return
|
||||||
|
|||||||
Reference in New Issue
Block a user