Update billing status in Delivery Note if directly invoiced against SO
This commit is contained in:
parent
a3e87c7af1
commit
5b13b99441
@ -12,6 +12,8 @@ from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note \
|
||||
import update_billing_amount_based_on_so, update_billing_percentage
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@ -98,6 +100,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_in_dn()
|
||||
|
||||
# this sequence because outstanding may get -ve
|
||||
self.make_gl_entries()
|
||||
@ -110,6 +113,7 @@ class SalesInvoice(SellingController):
|
||||
self.update_against_document_in_jv()
|
||||
|
||||
self.update_time_log_batch(self.name)
|
||||
|
||||
|
||||
def before_cancel(self):
|
||||
self.update_time_log_batch(None)
|
||||
@ -129,6 +133,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_in_dn()
|
||||
|
||||
if not self.is_return:
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
@ -629,6 +634,18 @@ class SalesInvoice(SellingController):
|
||||
"cost_center": self.write_off_cost_center
|
||||
}, write_off_account_currency)
|
||||
)
|
||||
|
||||
def update_billing_status_in_dn(self):
|
||||
updated_delivery_notes = []
|
||||
for d in self.get("items"):
|
||||
if d.dn_detail and not d.so_detail:
|
||||
frappe.db.set_value("Delivery Note Item", d.dn_detail, "billed_amt", d.amount)
|
||||
updated_delivery_notes.append(d.delivery_note)
|
||||
elif d.so_detail:
|
||||
updated_delivery_notes += update_billing_amount_based_on_so(d.so_detail)
|
||||
|
||||
for dn in set(updated_delivery_notes):
|
||||
update_billing_percentage(dn)
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
@ -698,4 +715,4 @@ def make_delivery_note(source_name, target_doc=None):
|
||||
@frappe.whitelist()
|
||||
def make_sales_return(source_name, target_doc=None):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
return make_return_doc("Sales Invoice", source_name, target_doc)
|
||||
return make_return_doc("Sales Invoice", source_name, target_doc)
|
@ -52,7 +52,8 @@ status_map = {
|
||||
],
|
||||
"Delivery Note": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
||||
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
["Closed", "eval:self.status=='Closed'"],
|
||||
],
|
||||
@ -153,7 +154,6 @@ class StatusUpdater(Document):
|
||||
# check if overflow is within tolerance
|
||||
tolerance, self.tolerance, self.global_tolerance = get_tolerance_for(item['item_code'],
|
||||
self.tolerance, self.global_tolerance)
|
||||
|
||||
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
|
||||
item[args['target_ref_field']]) * 100
|
||||
|
||||
|
@ -52,16 +52,8 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
}
|
||||
|
||||
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1
|
||||
&& !doc.is_return && doc.status!="Closed") {
|
||||
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
|
||||
var from_sales_invoice = false;
|
||||
from_sales_invoice = cur_frm.doc.items.some(function(item) {
|
||||
return item.against_sales_invoice ? true : false;
|
||||
});
|
||||
|
||||
if(!from_sales_invoice)
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||
if(doc.docstatus==1 && !doc.is_return && doc.status!="Closed" && flt(doc.per_billed, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||
}
|
||||
|
||||
if(doc.docstatus==1 && doc.status === "Closed" && this.frm.has_perm("submit")) {
|
||||
@ -285,6 +277,3 @@ if (sys_defaults.auto_accounting_for_stock) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -2154,6 +2154,30 @@
|
||||
"unique": 0,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "per_billed",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "% Amount Billed",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -2317,7 +2341,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nDraft\nSubmitted\nCancelled\nClosed",
|
||||
"options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
@ -2672,7 +2696,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2015-12-17 16:19:27.004718",
|
||||
"modified": "2015-12-25 16:20:39.014291",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
@ -48,7 +48,8 @@ class DeliveryNote(SellingController):
|
||||
'target_ref_field': 'qty',
|
||||
'source_field': 'qty',
|
||||
'percent_join_field': 'against_sales_invoice',
|
||||
'overflow_type': 'delivery'
|
||||
'overflow_type': 'delivery',
|
||||
'no_tolerance': 1
|
||||
},
|
||||
{
|
||||
'source_dt': 'Delivery Note Item',
|
||||
@ -56,21 +57,11 @@ class DeliveryNote(SellingController):
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
# 'target_parent_field': 'per_delivered',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'against_sales_order',
|
||||
# 'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
||||
}]
|
||||
|
||||
def onload(self):
|
||||
billed_qty = frappe.db.sql("""select sum(qty) from `tabSales Invoice Item`
|
||||
where docstatus=1 and delivery_note=%s""", self.name)
|
||||
if billed_qty:
|
||||
total_qty = sum((item.qty for item in self.get("items")))
|
||||
self.set_onload("billing_complete", (billed_qty[0][0] == total_qty))
|
||||
|
||||
def onload(self):
|
||||
self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Delivery Note",
|
||||
"is_return": 1, "return_against": self.name, "docstatus": 1})))
|
||||
|
||||
@ -199,6 +190,7 @@ class DeliveryNote(SellingController):
|
||||
|
||||
# update delivered qty in sales order
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status()
|
||||
|
||||
if not self.is_return:
|
||||
self.check_credit_limit()
|
||||
@ -206,18 +198,15 @@ class DeliveryNote(SellingController):
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
|
||||
frappe.db.set(self, 'status', 'Submitted')
|
||||
|
||||
|
||||
def on_cancel(self):
|
||||
self.check_stop_or_close_sales_order("against_sales_order")
|
||||
self.check_next_docstatus()
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status()
|
||||
|
||||
self.update_stock_ledger()
|
||||
|
||||
frappe.db.set(self, 'status', 'Cancelled')
|
||||
self.cancel_packing_slips()
|
||||
|
||||
self.make_gl_entries_on_cancel()
|
||||
@ -279,6 +268,73 @@ class DeliveryNote(SellingController):
|
||||
self.set_status(update=True, status=status)
|
||||
self.notify_update()
|
||||
clear_doctype_notifications(self)
|
||||
|
||||
def update_billing_status(self):
|
||||
updated_delivery_notes = [self.name]
|
||||
for d in self.get("items"):
|
||||
if d.si_detail and not d.so_detail:
|
||||
d.billed_amt = d.amount
|
||||
frappe.db.set(d, 'billed_amt', d.amount)
|
||||
elif d.so_detail:
|
||||
updated_delivery_notes += update_billing_amount_based_on_so(d.so_detail)
|
||||
d.billed_amt = frappe.db.get_value("Delivery Note Item", d.name, "billed_amt")
|
||||
|
||||
for dn in set(updated_delivery_notes):
|
||||
update_billing_percentage(dn)
|
||||
self.per_billed = frappe.db.get_value("Delivery Note", self.name, "per_billed")
|
||||
|
||||
def update_billing_percentage(delivery_note, set_modified=True):
|
||||
frappe.db.sql("""update `tabDelivery Note`
|
||||
set per_billed = round(ifnull(
|
||||
(select ifnull(sum(if(amount > billed_amt, billed_amt, amount)), 0) * 100 / sum(amount)
|
||||
from `tabDelivery Note Item` where parent=%s), 0), 2)
|
||||
, modified = now(), modified_by = %s
|
||||
where name=%s""", (delivery_note, frappe.session.user, delivery_note))
|
||||
|
||||
if set_modified:
|
||||
dn = frappe.get_doc("Delivery Note", delivery_note)
|
||||
dn.set_status(update=True)
|
||||
dn.notify_update()
|
||||
|
||||
def update_billing_amount_based_on_so(so_detail):
|
||||
# Billed against Sales Order directly
|
||||
billed_against_so = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item`
|
||||
where so_detail=%s and (dn_detail is null or dn_detail = '') and docstatus=1""", so_detail)
|
||||
billed_against_so = billed_against_so and billed_against_so[0][0] or 0
|
||||
|
||||
# Get all Delivery Note Item rows against the Sales Order Item row
|
||||
dn_details = frappe.db.sql("""select dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent
|
||||
from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn
|
||||
where dn.name=dn_item.parent and dn_item.so_detail=%s
|
||||
and dn.docstatus=1 and dn.is_return = 0
|
||||
order by dn.posting_date asc, dn.posting_time asc, dn.name asc""", so_detail, as_dict=1)
|
||||
|
||||
updated_dn = []
|
||||
for dnd in dn_details:
|
||||
billed_amt_agianst_dn = 0
|
||||
|
||||
# If delivered against Sales Invoice
|
||||
if dnd.si_detail:
|
||||
billed_amt_agianst_dn = flt(dnd.amount)
|
||||
billed_against_so -= billed_amt_agianst_dn
|
||||
else:
|
||||
# Get billed amount directly against Delivery Note
|
||||
billed_amt_agianst_dn = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item`
|
||||
where dn_detail=%s and docstatus=1""", dnd.name)
|
||||
billed_amt_agianst_dn = billed_amt_agianst_dn and billed_amt_agianst_dn[0][0] or 0
|
||||
|
||||
# Distribute billed amount directly against SO between DNs based on FIFO
|
||||
if billed_against_so and billed_amt_agianst_dn < dnd.amount:
|
||||
pending_to_bill = flt(dnd.amount) - billed_amt_agianst_dn
|
||||
if pending_to_bill <= billed_against_so:
|
||||
billed_amt_agianst_dn += pending_to_bill
|
||||
else:
|
||||
billed_amt_agianst_dn += billed_against_so
|
||||
|
||||
frappe.db.set_value("Delivery Note Item", dnd.name, "billed_amt", billed_amt_agianst_dn)
|
||||
updated_dn.append(dnd.parent)
|
||||
|
||||
return updated_dn
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
|
@ -6,6 +6,10 @@ frappe.listview_settings['Delivery Note'] = {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(doc.status==="Closed") {
|
||||
return [__("Closed"), "green", "status,=,Closed"];
|
||||
}
|
||||
} else if (flt(doc.per_billed, 2) < 100) {
|
||||
return [__("To Bill"), "orange", "per_billed,<,100"];
|
||||
} else if (flt(doc.per_billed, 2) == 100) {
|
||||
return [__("Completed"), "green", "per_billed,=,100"];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user