Billing status in Purchase Receipt

This commit is contained in:
Nabin Hait 2015-12-30 19:08:11 +05:30 committed by Anand Doshi
parent ba5d6b5004
commit bdab0eea0f
8 changed files with 1595 additions and 1480 deletions

View File

@ -11,6 +11,7 @@ import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_amount_based_on_po
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -246,6 +247,7 @@ class PurchaseInvoice(BuyingController):
self.update_against_document_in_jv()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
self.update_project()
@ -407,6 +409,8 @@ class PurchaseInvoice(BuyingController):
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
self.make_gl_entries_on_cancel()
self.update_project()
@ -430,6 +434,18 @@ class PurchaseInvoice(BuyingController):
"fiscal_year": self.fiscal_year, "name": ("!=", self.name), "docstatus": ("<", 2)})
if pi:
frappe.throw("Supplier Invoice No exists in Purchase Invoice {0}".format(pi))
def update_billing_status_in_pr(self, set_modified=True):
updated_pr = []
for d in self.get("items"):
if d.pr_detail and not d.po_detail:
frappe.db.set_value("Purchase Receipt Item", d.pr_detail, "billed_amt", d.amount)
updated_pr.append(d.purchase_receipt)
elif d.po_detail:
updated_pr += update_billing_amount_based_on_po(d.po_detail)
for pr in set(updated_pr):
frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(set_modified=set_modified)
@frappe.whitelist()
def get_expense_account(doctype, txt, searchfield, start, page_len, filters):

View File

@ -12,8 +12,7 @@ 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
from erpnext.stock.doctype.delivery_note.delivery_note import update_billing_amount_based_on_so
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -635,7 +634,7 @@ class SalesInvoice(SellingController):
}, write_off_account_currency)
)
def update_billing_status_in_dn(self):
def update_billing_status_in_dn(self, set_modified=True):
updated_delivery_notes = []
for d in self.get("items"):
if d.dn_detail and not d.so_detail:
@ -645,7 +644,7 @@ class SalesInvoice(SellingController):
updated_delivery_notes += update_billing_amount_based_on_so(d.so_detail)
for dn in set(updated_delivery_notes):
update_billing_percentage(dn)
frappe.get_doc("Delivery Note", dn).update_billing_percentage(set_modified=set_modified)
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context

View File

@ -59,7 +59,8 @@ status_map = {
],
"Purchase Receipt": [
["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'"],
]
@ -178,15 +179,10 @@ class StatusUpdater(Document):
else:
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
args['set_modified'] = ''
if change_modified:
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
.format(frappe.db.escape(frappe.session.user))
self._update_children(args)
if "percent_join_field" in args:
self._update_percent_field(args)
self._update_percent_field_in_targets(args, change_modified)
def _update_children(self, args):
"""Update quantities or amount in child table"""
@ -216,40 +212,47 @@ class StatusUpdater(Document):
from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
and (docstatus=1 %(cond)s) %(extra_cond)s) %(second_source_condition)s
where name='%(detail_id)s'""" % args)
def _update_percent_field(self, args):
def _update_percent_field_in_targets(self, args, change_modified=True):
"""Update percent field in parent transaction"""
unique_transactions = set([d.get(args['percent_join_field']) for d in self.get_all_children(args['source_dt'])])
distinct_transactions = set([d.get(args['percent_join_field'])
for d in self.get_all_children(args['source_dt'])])
for name in unique_transactions:
if not name:
continue
for name in distinct_transactions:
if name:
args['name'] = name
self._update_percent_field(args, change_modified)
args['name'] = name
def _update_percent_field(self, args, change_modified=True):
"""Update percent field in parent transaction"""
args['set_modified'] = ''
if change_modified:
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
.format(frappe.db.escape(frappe.session.user))
if args.get('target_parent_field'):
frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(target_parent_field)s = round(
ifnull((select
ifnull(sum(if(%(target_ref_field)s > %(target_field)s, %(target_field)s, %(target_ref_field)s)), 0)
/ sum(%(target_ref_field)s) * 100
from `tab%(target_dt)s` where parent="%(name)s"), 0), 2)
%(set_modified)s
where name='%(name)s'""" % args)
# update percent complete in the parent table
if args.get('target_parent_field'):
frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(target_parent_field)s = round(
ifnull((select
ifnull(sum(if(%(target_ref_field)s > %(target_field)s, %(target_field)s, %(target_ref_field)s)), 0)
/ sum(%(target_ref_field)s) * 100
from `tab%(target_dt)s` where parent="%(name)s"), 0), 2)
%(set_modified)s
where name='%(name)s'""" % args)
# update field
if args.get('status_field'):
frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(status_field)s = if(%(target_parent_field)s<0.001,
'Not %(keyword)s', if(%(target_parent_field)s>=99.99,
'Fully %(keyword)s', 'Partly %(keyword)s'))
where name='%(name)s'""" % args)
# update field
if args.get('status_field'):
frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(status_field)s = if(%(target_parent_field)s<0.001,
'Not %(keyword)s', if(%(target_parent_field)s>=99.99,
'Fully %(keyword)s', 'Partly %(keyword)s'))
where name='%(name)s'""" % args)
if args.get("set_modified"):
target = frappe.get_doc(args["target_parent_dt"], name)
target.set_status(update=True)
target.notify_update()
if args.get("set_modified"):
target = frappe.get_doc(args["target_parent_dt"], args["name"])
target.set_status(update=True)
target.notify_update()
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
ref_fieldname = ref_dt.lower().replace(" ", "_")

View File

@ -312,6 +312,16 @@ class StockController(AccountsController):
for w in warehouses:
validate_warehouse_company(w, self.company)
def update_billing_percentage(self, set_modified=True):
self._update_percent_field({
"target_dt": self.doctype + " Item",
"target_parent_dt": self.doctype,
"target_parent_field": "per_billed",
"target_ref_field": "amount",
"target_field": "billed_amt",
"name": self.name,
}, set_modified)
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None):

View File

@ -269,33 +269,19 @@ class DeliveryNote(SellingController):
self.notify_update()
clear_doctype_notifications(self)
def update_billing_status(self):
def update_billing_status(self, set_modified=True):
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)
dn_doc = self if (dn == self.name) else frappe.get_doc("Delivery Note", dn)
dn_doc.update_billing_percentage(set_modified=set_modified)
self.load_from_db()
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

View File

@ -1812,7 +1812,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,
@ -1930,6 +1930,30 @@
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "per_billed",
"fieldtype": "Percent",
"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,
@ -2278,7 +2302,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2015-12-17 16:18:48.969643",
"modified": "2015-12-30 18:15:06.678001",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",

View File

@ -242,6 +242,8 @@ class PurchaseReceipt(BuyingController):
self.update_prevdoc_status()
self.update_ordered_qty()
self.update_billing_status()
if not self.is_return:
purchase_controller.update_last_purchase_rate(self, 1)
@ -280,6 +282,8 @@ class PurchaseReceipt(BuyingController):
self.update_prevdoc_status()
# Must be called after updating received qty in PO
self.update_ordered_qty()
self.update_billing_status()
if not self.is_return:
pc_obj.update_last_purchase_rate(self, 0)
@ -435,6 +439,55 @@ class PurchaseReceipt(BuyingController):
self.set_status(update=True, status = status)
self.notify_update()
clear_doctype_notifications(self)
def update_billing_status(self, set_modified=True):
updated_pr = [self.name]
for d in self.get("items"):
if d.prevdoc_detail_docname:
updated_pr += update_billing_amount_based_on_po(d.prevdoc_detail_docname)
d.billed_amt = frappe.db.get_value("Purchase Receipt Item", d.name, "billed_amt")
for pr in set(updated_pr):
pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
pr_doc.update_billing_percentage(set_modified=set_modified)
self.load_from_db()
def update_billing_amount_based_on_po(po_detail):
# Billed against Sales Order directly
billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""", po_detail)
billed_against_po = billed_against_po and billed_against_po[0][0] or 0
# Get all Delivery Note Item rows against the Sales Order Item row
pr_details = frappe.db.sql("""select pr_item.name, pr_item.amount, pr_item.parent
from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
where pr.name=pr_item.parent and pr_item.prevdoc_detail_docname=%s
and pr.docstatus=1 and pr.is_return = 0
order by pr.posting_date asc, pr.posting_time asc, pr.name asc""", po_detail, as_dict=1)
updated_pr = []
for pr_item in pr_details:
# Get billed amount directly against Purchase Receipt
billed_amt_agianst_pr = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
where pr_detail=%s and docstatus=1""", pr_item.name)
billed_amt_agianst_pr = billed_amt_agianst_pr and billed_amt_agianst_pr[0][0] or 0
# Distribute billed amount directly against PO between PRs based on FIFO
if billed_against_po and billed_amt_agianst_pr < pr_item.amount:
pending_to_bill = flt(pr_item.amount) - billed_amt_agianst_pr
if pending_to_bill <= billed_against_po:
billed_amt_agianst_pr += pending_to_bill
billed_against_po -= pending_to_bill
else:
billed_amt_agianst_pr += billed_against_po
billed_against_po = 0
frappe.db.set_value("Purchase Receipt Item", pr_item.name, "billed_amt", billed_amt_agianst_pr)
updated_pr.append(pr_item.parent)
return updated_pr
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):