- Fixed logic of reserved qty calculation while delivered product bundle via Sales Invoice
- Delete stock ledger entries on cancellation of Sales Invoice while product bundle delivered - Make packing list on validate
This commit is contained in:
parent
43e46a8506
commit
453cc374d4
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.utils import cint, cstr, flt
|
from frappe.utils import cint, flt
|
||||||
from frappe import _, msgprint, throw
|
from frappe import _, msgprint, throw
|
||||||
from erpnext.accounts.party import get_party_account, get_due_date
|
from erpnext.accounts.party import get_party_account, get_due_date
|
||||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||||
@ -66,6 +66,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.validate_c_form()
|
self.validate_c_form()
|
||||||
self.validate_time_logs_are_submitted()
|
self.validate_time_logs_are_submitted()
|
||||||
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
|
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
|
||||||
|
self.update_packing_list()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
super(SalesInvoice, self).on_submit()
|
super(SalesInvoice, self).on_submit()
|
||||||
@ -363,6 +364,13 @@ class SalesInvoice(SellingController):
|
|||||||
d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
|
d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
|
||||||
d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
|
d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
|
||||||
|
|
||||||
|
def update_packing_list(self):
|
||||||
|
if cint(self.update_stock) == 1:
|
||||||
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
|
make_packing_list(self, 'items')
|
||||||
|
else:
|
||||||
|
self.set('packed_items', [])
|
||||||
|
|
||||||
|
|
||||||
def get_warehouse(self):
|
def get_warehouse(self):
|
||||||
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
|
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
|
||||||
@ -381,20 +389,6 @@ class SalesInvoice(SellingController):
|
|||||||
return warehouse
|
return warehouse
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
if cint(self.update_stock) == 1:
|
|
||||||
# Set default warehouse from POS Profile
|
|
||||||
if cint(self.is_pos) == 1:
|
|
||||||
w = self.get_warehouse()
|
|
||||||
if w:
|
|
||||||
for d in self.get('items'):
|
|
||||||
if not d.warehouse:
|
|
||||||
d.warehouse = cstr(w)
|
|
||||||
|
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
|
||||||
make_packing_list(self, 'items')
|
|
||||||
else:
|
|
||||||
self.set('packed_items', [])
|
|
||||||
|
|
||||||
if cint(self.is_pos) == 1:
|
if cint(self.is_pos) == 1:
|
||||||
if flt(self.paid_amount) == 0:
|
if flt(self.paid_amount) == 0:
|
||||||
if self.cash_bank_account:
|
if self.cash_bank_account:
|
||||||
@ -424,8 +418,9 @@ class SalesInvoice(SellingController):
|
|||||||
def update_stock_ledger(self):
|
def update_stock_ledger(self):
|
||||||
sl_entries = []
|
sl_entries = []
|
||||||
for d in self.get_item_list():
|
for d in self.get_item_list():
|
||||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \
|
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and d.warehouse and flt(d['qty']):
|
||||||
and d.warehouse:
|
self.update_reserved_qty(d)
|
||||||
|
|
||||||
incoming_rate = 0
|
incoming_rate = 0
|
||||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||||
incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||||
|
2
erpnext/change_log/current/packing_list.md
Normal file
2
erpnext/change_log/current/packing_list.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
- Fixed logic of reserved qty calculation while delivered product bundle via Sales Invoice
|
||||||
|
- Delete stock ledger entries on cancellation of Sales Invoice while product bundle delivered
|
@ -174,12 +174,14 @@ class SellingController(StockController):
|
|||||||
if flt(d.qty) > flt(d.delivered_qty):
|
if flt(d.qty) > flt(d.delivered_qty):
|
||||||
reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
|
reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
|
||||||
|
|
||||||
elif self.doctype == "Delivery Note" and d.against_sales_order and not self.is_return:
|
elif (((self.doctype == "Delivery Note" and d.against_sales_order)
|
||||||
|
or (self.doctype == "Sales Invoice" and d.sales_order and self.update_stock))
|
||||||
|
and not self.is_return):
|
||||||
# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
|
# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
|
||||||
# But in this case reserved qty should only be reduced by 10 and not 12
|
# But in this case reserved qty should only be reduced by 10 and not 12
|
||||||
|
|
||||||
already_delivered_qty = self.get_already_delivered_qty(self.name,
|
already_delivered_qty = self.get_already_delivered_qty(self.name,
|
||||||
d.against_sales_order, d.so_detail)
|
d.against_sales_order if self.doctype=="Delivery Note" else d.sales_order, d.so_detail)
|
||||||
so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.so_detail)
|
so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.so_detail)
|
||||||
|
|
||||||
if already_delivered_qty + d.qty > so_qty:
|
if already_delivered_qty + d.qty > so_qty:
|
||||||
@ -221,12 +223,21 @@ class SellingController(StockController):
|
|||||||
return frappe.db.sql("""select name from `tabProduct Bundle`
|
return frappe.db.sql("""select name from `tabProduct Bundle`
|
||||||
where new_item_code=%s and docstatus != 2""", item_code)
|
where new_item_code=%s and docstatus != 2""", item_code)
|
||||||
|
|
||||||
def get_already_delivered_qty(self, dn, so, so_detail):
|
def get_already_delivered_qty(self, current_docname, so, so_detail):
|
||||||
qty = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
|
delivered_via_dn = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
|
||||||
where so_detail = %s and docstatus = 1
|
where so_detail = %s and docstatus = 1
|
||||||
and against_sales_order = %s
|
and against_sales_order = %s
|
||||||
and parent != %s""", (so_detail, so, dn))
|
and parent != %s""", (so_detail, so, current_docname))
|
||||||
return qty and flt(qty[0][0]) or 0.0
|
|
||||||
|
delivered_via_si = frappe.db.sql("""select sum(qty) from `tabSales Invoice Item`
|
||||||
|
where so_detail = %s and docstatus = 1
|
||||||
|
and sales_order = %s
|
||||||
|
and parent != %s""", (so_detail, so, current_docname))
|
||||||
|
|
||||||
|
total_delivered_qty = (flt(delivered_via_dn[0][0]) if delivered_via_dn else 0) \
|
||||||
|
+ (flt(delivered_via_si[0][0]) if delivered_via_si else 0)
|
||||||
|
|
||||||
|
return total_delivered_qty
|
||||||
|
|
||||||
def get_so_qty_and_warehouse(self, so_detail):
|
def get_so_qty_and_warehouse(self, so_detail):
|
||||||
so_item = frappe.db.sql("""select qty, warehouse from `tabSales Order Item`
|
so_item = frappe.db.sql("""select qty, warehouse from `tabSales Order Item`
|
||||||
|
@ -9,6 +9,8 @@ import frappe.defaults
|
|||||||
|
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||||
|
from erpnext.stock.utils import update_bin
|
||||||
|
|
||||||
|
|
||||||
class StockController(AccountsController):
|
class StockController(AccountsController):
|
||||||
def make_gl_entries(self, repost_future_gle=True):
|
def make_gl_entries(self, repost_future_gle=True):
|
||||||
@ -227,6 +229,23 @@ class StockController(AccountsController):
|
|||||||
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
|
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
|
||||||
|
|
||||||
return incoming_rate
|
return incoming_rate
|
||||||
|
|
||||||
|
def update_reserved_qty(self, d):
|
||||||
|
if d['reserved_qty'] < 0 :
|
||||||
|
# Reduce reserved qty from reserved warehouse mentioned in so
|
||||||
|
if not d["reserved_warehouse"]:
|
||||||
|
frappe.throw(_("Reserved Warehouse is missing in Sales Order"))
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"item_code": d['item_code'],
|
||||||
|
"warehouse": d["reserved_warehouse"],
|
||||||
|
"voucher_type": self.doctype,
|
||||||
|
"voucher_no": self.name,
|
||||||
|
"reserved_qty": (self.docstatus==1 and 1 or -1)*flt(d['reserved_qty']),
|
||||||
|
"posting_date": self.posting_date,
|
||||||
|
"is_amended": self.amended_from and 'Yes' or 'No'
|
||||||
|
}
|
||||||
|
update_bin(args)
|
||||||
|
|
||||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||||
warehouse_account=None):
|
warehouse_account=None):
|
||||||
|
@ -180,3 +180,4 @@ execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=
|
|||||||
erpnext.patches.v5_1.rename_roles
|
erpnext.patches.v5_1.rename_roles
|
||||||
erpnext.patches.v5_1.default_bom
|
erpnext.patches.v5_1.default_bom
|
||||||
execute:frappe.delete_doc("DocType", "Party Type")
|
execute:frappe.delete_doc("DocType", "Party Type")
|
||||||
|
erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items
|
||||||
|
0
erpnext/patches/v5_4/__init__.py
Normal file
0
erpnext/patches/v5_4/__init__.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from erpnext.utilities.repost_stock import update_bin_qty, get_reserved_qty, repost_actual_qty
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice`
|
||||||
|
where docstatus = 2 and ifnull(update_stock, 0) = 1""")
|
||||||
|
|
||||||
|
if cancelled_invoices:
|
||||||
|
frappe.db.sql("""delete from `tabStock Ledger Entry`
|
||||||
|
where voucher_type = 'Sales Invoice' and voucher_no in (%s)"""
|
||||||
|
% (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices))
|
||||||
|
|
||||||
|
for item_code, warehouse in frappe.db.sql("select item_code, warehouse from tabBin where ifnull(reserved_qty, 0) < 0"):
|
||||||
|
|
||||||
|
repost_actual_qty(item_code, warehouse)
|
||||||
|
|
||||||
|
update_bin_qty(item_code, warehouse, {
|
||||||
|
"reserved_qty": get_reserved_qty(item_code, warehouse)
|
||||||
|
})
|
||||||
|
|
@ -9,7 +9,6 @@ from frappe.utils import flt, cint
|
|||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from erpnext.stock.utils import update_bin
|
|
||||||
from erpnext.controllers.selling_controller import SellingController
|
from erpnext.controllers.selling_controller import SellingController
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
@ -262,23 +261,6 @@ class DeliveryNote(SellingController):
|
|||||||
|
|
||||||
self.make_sl_entries(sl_entries)
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
def update_reserved_qty(self, d):
|
|
||||||
if d['reserved_qty'] < 0 :
|
|
||||||
# Reduce reserved qty from reserved warehouse mentioned in so
|
|
||||||
if not d["reserved_warehouse"]:
|
|
||||||
frappe.throw(_("Reserved Warehouse is missing in Sales Order"))
|
|
||||||
|
|
||||||
args = {
|
|
||||||
"item_code": d['item_code'],
|
|
||||||
"warehouse": d["reserved_warehouse"],
|
|
||||||
"voucher_type": self.doctype,
|
|
||||||
"voucher_no": self.name,
|
|
||||||
"reserved_qty": (self.docstatus==1 and 1 or -1)*flt(d['reserved_qty']),
|
|
||||||
"posting_date": self.posting_date,
|
|
||||||
"is_amended": self.amended_from and 'Yes' or 'No'
|
|
||||||
}
|
|
||||||
update_bin(args)
|
|
||||||
|
|
||||||
def get_list_context(context=None):
|
def get_list_context(context=None):
|
||||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||||
list_context = get_list_context(context)
|
list_context = get_list_context(context)
|
||||||
|
Loading…
Reference in New Issue
Block a user