Fixed rest of the test cases frappe/frapp#478
This commit is contained in:
parent
d29465029d
commit
9fd50bcfb6
@ -17,17 +17,20 @@ from erpnext.accounts.party import get_party_account, get_due_date
|
|||||||
class PurchaseInvoice(BuyingController):
|
class PurchaseInvoice(BuyingController):
|
||||||
tname = 'Purchase Invoice Item'
|
tname = 'Purchase Invoice Item'
|
||||||
fname = 'entries'
|
fname = 'entries'
|
||||||
status_updater = [{
|
|
||||||
'source_dt': 'Purchase Invoice Item',
|
def __init__(self, arg1, arg2=None):
|
||||||
'target_dt': 'Purchase Order Item',
|
super(PurchaseInvoice, self).__init__(arg1, arg2)
|
||||||
'join_field': 'po_detail',
|
self.status_updater = [{
|
||||||
'target_field': 'billed_amt',
|
'source_dt': 'Purchase Invoice Item',
|
||||||
'target_parent_dt': 'Purchase Order',
|
'target_dt': 'Purchase Order Item',
|
||||||
'target_parent_field': 'per_billed',
|
'join_field': 'po_detail',
|
||||||
'target_ref_field': 'amount',
|
'target_field': 'billed_amt',
|
||||||
'source_field': 'amount',
|
'target_parent_dt': 'Purchase Order',
|
||||||
'percent_join_field': 'purchase_order',
|
'target_parent_field': 'per_billed',
|
||||||
}]
|
'target_ref_field': 'amount',
|
||||||
|
'source_field': 'amount',
|
||||||
|
'percent_join_field': 'purchase_order',
|
||||||
|
}]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.is_opening:
|
if not self.is_opening:
|
||||||
|
@ -20,20 +20,23 @@ from erpnext.controllers.selling_controller import SellingController
|
|||||||
class SalesInvoice(SellingController):
|
class SalesInvoice(SellingController):
|
||||||
tname = 'Sales Invoice Item'
|
tname = 'Sales Invoice Item'
|
||||||
fname = 'entries'
|
fname = 'entries'
|
||||||
status_updater = [{
|
|
||||||
'source_dt': 'Sales Invoice Item',
|
def __init__(self, arg1, arg2=None):
|
||||||
'target_field': 'billed_amt',
|
super(SalesInvoice, self).__init__(arg1, arg2)
|
||||||
'target_ref_field': 'amount',
|
self.status_updater = [{
|
||||||
'target_dt': 'Sales Order Item',
|
'source_dt': 'Sales Invoice Item',
|
||||||
'join_field': 'so_detail',
|
'target_field': 'billed_amt',
|
||||||
'target_parent_dt': 'Sales Order',
|
'target_ref_field': 'amount',
|
||||||
'target_parent_field': 'per_billed',
|
'target_dt': 'Sales Order Item',
|
||||||
'source_field': 'amount',
|
'join_field': 'so_detail',
|
||||||
'join_field': 'so_detail',
|
'target_parent_dt': 'Sales Order',
|
||||||
'percent_join_field': 'sales_order',
|
'target_parent_field': 'per_billed',
|
||||||
'status_field': 'billing_status',
|
'source_field': 'amount',
|
||||||
'keyword': 'Billed'
|
'join_field': 'so_detail',
|
||||||
}]
|
'percent_join_field': 'sales_order',
|
||||||
|
'status_field': 'billing_status',
|
||||||
|
'keyword': 'Billed'
|
||||||
|
}]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(SalesInvoice, self).validate()
|
super(SalesInvoice, self).validate()
|
||||||
|
@ -11,13 +11,13 @@ from erpnext.stock.doctype.item.item import get_last_purchase_details
|
|||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
|
|
||||||
class PurchaseCommon(BuyingController):
|
class PurchaseCommon(BuyingController):
|
||||||
|
|
||||||
def update_last_purchase_rate(self, obj, is_submit):
|
def update_last_purchase_rate(self, obj, is_submit):
|
||||||
"""updates last_purchase_rate in item table for each item"""
|
"""updates last_purchase_rate in item table for each item"""
|
||||||
|
|
||||||
import frappe.utils
|
import frappe.utils
|
||||||
this_purchase_date = frappe.utils.getdate(obj.get('posting_date') or obj.get('transaction_date'))
|
this_purchase_date = frappe.utils.getdate(obj.get('posting_date') or obj.get('transaction_date'))
|
||||||
|
|
||||||
for d in obj.get(obj.fname):
|
for d in obj.get(obj.fname):
|
||||||
# get last purchase details
|
# get last purchase details
|
||||||
last_purchase_details = get_last_purchase_details(d.item_code, obj.name)
|
last_purchase_details = get_last_purchase_details(d.item_code, obj.name)
|
||||||
@ -33,19 +33,19 @@ class PurchaseCommon(BuyingController):
|
|||||||
if flt(d.conversion_factor):
|
if flt(d.conversion_factor):
|
||||||
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
|
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Row ") + cstr(d.idx) + ": " +
|
frappe.throw(_("Row ") + cstr(d.idx) + ": " +
|
||||||
_("UOM Conversion Factor is mandatory"))
|
_("UOM Conversion Factor is mandatory"))
|
||||||
|
|
||||||
# update last purchsae rate
|
# update last purchsae rate
|
||||||
if last_purchase_rate:
|
if last_purchase_rate:
|
||||||
frappe.db.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
|
frappe.db.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
|
||||||
(flt(last_purchase_rate), d.item_code))
|
(flt(last_purchase_rate), d.item_code))
|
||||||
|
|
||||||
def get_last_purchase_rate(self, obj):
|
def get_last_purchase_rate(self, obj):
|
||||||
"""get last purchase rates for all items"""
|
"""get last purchase rates for all items"""
|
||||||
doc_name = obj.name
|
doc_name = obj.name
|
||||||
conversion_rate = flt(obj.get('conversion_rate')) or 1.0
|
conversion_rate = flt(obj.get('conversion_rate')) or 1.0
|
||||||
|
|
||||||
for d in obj.get(obj.fname):
|
for d in obj.get(obj.fname):
|
||||||
if d.item_code:
|
if d.item_code:
|
||||||
last_purchase_details = get_last_purchase_details(d.item_code, doc_name)
|
last_purchase_details = get_last_purchase_details(d.item_code, doc_name)
|
||||||
@ -59,113 +59,113 @@ class PurchaseCommon(BuyingController):
|
|||||||
else:
|
else:
|
||||||
# if no last purchase found, reset all values to 0
|
# if no last purchase found, reset all values to 0
|
||||||
d.base_price_list_rate = d.base_rate = d.price_list_rate = d.rate = d.discount_percentage = 0
|
d.base_price_list_rate = d.base_rate = d.price_list_rate = d.rate = d.discount_percentage = 0
|
||||||
|
|
||||||
item_last_purchase_rate = frappe.db.get_value("Item",
|
item_last_purchase_rate = frappe.db.get_value("Item",
|
||||||
d.item_code, "last_purchase_rate")
|
d.item_code, "last_purchase_rate")
|
||||||
if item_last_purchase_rate:
|
if item_last_purchase_rate:
|
||||||
d.base_price_list_rate = d.base_rate = d.price_list_rate \
|
d.base_price_list_rate = d.base_rate = d.price_list_rate \
|
||||||
= d.rate = item_last_purchase_rate
|
= d.rate = item_last_purchase_rate
|
||||||
|
|
||||||
def validate_for_items(self, obj):
|
def validate_for_items(self, obj):
|
||||||
check_list, chk_dupl_itm=[],[]
|
check_list, chk_dupl_itm=[],[]
|
||||||
for d in obj.get(obj.fname):
|
for d in obj.get(obj.fname):
|
||||||
# validation for valid qty
|
# validation for valid qty
|
||||||
if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)):
|
if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)):
|
||||||
frappe.throw("Please enter valid qty for item %s" % cstr(d.item_code))
|
frappe.throw("Please enter valid qty for item %s" % cstr(d.item_code))
|
||||||
|
|
||||||
# udpate with latest quantities
|
# udpate with latest quantities
|
||||||
bin = frappe.db.sql("""select projected_qty from `tabBin` where
|
bin = frappe.db.sql("""select projected_qty from `tabBin` where
|
||||||
item_code = %s and warehouse = %s""", (d.item_code, d.warehouse), as_dict=1)
|
item_code = %s and warehouse = %s""", (d.item_code, d.warehouse), as_dict=1)
|
||||||
|
|
||||||
f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
|
f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
|
||||||
if d.doctype == 'Purchase Receipt Item':
|
if d.doctype == 'Purchase Receipt Item':
|
||||||
f_lst.pop('received_qty')
|
f_lst.pop('received_qty')
|
||||||
for x in f_lst :
|
for x in f_lst :
|
||||||
if d.meta.get_field(x):
|
if d.meta.get_field(x):
|
||||||
d.set(x, f_lst[x])
|
d.set(x, f_lst[x])
|
||||||
|
|
||||||
item = frappe.db.sql("""select is_stock_item, is_purchase_item,
|
item = frappe.db.sql("""select is_stock_item, is_purchase_item,
|
||||||
is_sub_contracted_item, end_of_life from `tabItem` where name=%s""", d.item_code)
|
is_sub_contracted_item, end_of_life from `tabItem` where name=%s""", d.item_code)
|
||||||
if not item:
|
if not item:
|
||||||
frappe.throw("Item %s does not exist in Item Master." % cstr(d.item_code))
|
frappe.throw("Item %s does not exist in Item Master." % cstr(d.item_code))
|
||||||
|
|
||||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||||
validate_end_of_life(d.item_code, item[0][3])
|
validate_end_of_life(d.item_code, item[0][3])
|
||||||
|
|
||||||
# validate stock item
|
# validate stock item
|
||||||
if item[0][0]=='Yes' and d.qty and not d.warehouse:
|
if item[0][0]=='Yes' and d.qty and not d.warehouse:
|
||||||
frappe.throw("Warehouse is mandatory for %s, since it is a stock item" % d.item_code)
|
frappe.throw("Warehouse is mandatory for %s, since it is a stock item" % d.item_code)
|
||||||
|
|
||||||
# validate purchase item
|
# validate purchase item
|
||||||
if item[0][1] != 'Yes' and item[0][2] != 'Yes':
|
if item[0][1] != 'Yes' and item[0][2] != 'Yes':
|
||||||
frappe.throw("Item %s is not a purchase item or sub-contracted item. Please check" % (d.item_code))
|
frappe.throw("Item %s is not a purchase item or sub-contracted item. Please check" % (d.item_code))
|
||||||
|
|
||||||
# list criteria that should not repeat if item is stock item
|
# list criteria that should not repeat if item is stock item
|
||||||
e = [getattr(d, "schedule_date", None), d.item_code, d.description, d.warehouse, d.uom,
|
e = [getattr(d, "schedule_date", None), d.item_code, d.description, d.warehouse, d.uom,
|
||||||
d.meta.get_field('prevdoc_docname') and d.prevdoc_docname or d.meta.get_field('sales_order_no') and d.sales_order_no or '',
|
d.meta.get_field('prevdoc_docname') and d.prevdoc_docname or d.meta.get_field('sales_order_no') and d.sales_order_no or '',
|
||||||
d.meta.get_field('prevdoc_detail_docname') and d.prevdoc_detail_docname or '',
|
d.meta.get_field('prevdoc_detail_docname') and d.prevdoc_detail_docname or '',
|
||||||
d.meta.get_field('batch_no') and d.batch_no or '']
|
d.meta.get_field('batch_no') and d.batch_no or '']
|
||||||
|
|
||||||
# if is not stock item
|
# if is not stock item
|
||||||
f = [getattr(d, "schedule_date", None), d.item_code, d.description]
|
f = [getattr(d, "schedule_date", None), d.item_code, d.description]
|
||||||
|
|
||||||
ch = frappe.db.sql("""select is_stock_item from `tabItem` where name = %s""", d.item_code)
|
ch = frappe.db.sql("""select is_stock_item from `tabItem` where name = %s""", d.item_code)
|
||||||
|
|
||||||
if ch and ch[0][0] == 'Yes':
|
if ch and ch[0][0] == 'Yes':
|
||||||
# check for same items
|
# check for same items
|
||||||
if e in check_list:
|
if e in check_list:
|
||||||
frappe.throw("""Item %s has been entered more than once with same description, schedule date, warehouse and uom.\n
|
frappe.throw("""Item %s has been entered more than once with same description, schedule date, warehouse and uom.\n
|
||||||
Please change any of the field value to enter the item twice""" % d.item_code)
|
Please change any of the field value to enter the item twice""" % d.item_code)
|
||||||
else:
|
else:
|
||||||
check_list.append(e)
|
check_list.append(e)
|
||||||
|
|
||||||
elif ch and ch[0][0] == 'No':
|
elif ch and ch[0][0] == 'No':
|
||||||
# check for same items
|
# check for same items
|
||||||
if f in chk_dupl_itm:
|
if f in chk_dupl_itm:
|
||||||
frappe.throw("""Item %s has been entered more than once with same description, schedule date.\n
|
frappe.throw("""Item %s has been entered more than once with same description, schedule date.\n
|
||||||
Please change any of the field value to enter the item twice.""" % d.item_code)
|
Please change any of the field value to enter the item twice.""" % d.item_code)
|
||||||
else:
|
else:
|
||||||
chk_dupl_itm.append(f)
|
chk_dupl_itm.append(f)
|
||||||
|
|
||||||
def get_qty(self, curr_doctype, ref_tab_fname, ref_tab_dn, ref_doc_tname, transaction, curr_parent_name):
|
def get_qty(self, curr_doctype, ref_tab_fname, ref_tab_dn, ref_doc_tname, transaction, curr_parent_name):
|
||||||
# Get total Quantities of current doctype (eg. PR) except for qty of this transaction
|
# Get total Quantities of current doctype (eg. PR) except for qty of this transaction
|
||||||
#------------------------------
|
#------------------------------
|
||||||
# please check as UOM changes from Material Request - Purchase Order ,so doing following else uom should be same .
|
# please check as UOM changes from Material Request - Purchase Order ,so doing following else uom should be same .
|
||||||
# i.e. in PO uom is NOS then in PR uom should be NOS
|
# i.e. in PO uom is NOS then in PR uom should be NOS
|
||||||
# but if in Material Request uom KG it can change in PO
|
# but if in Material Request uom KG it can change in PO
|
||||||
|
|
||||||
get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty'
|
get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty'
|
||||||
qty = frappe.db.sql("""select sum(%s) from `tab%s` where %s = %s and
|
qty = frappe.db.sql("""select sum(%s) from `tab%s` where %s = %s and
|
||||||
docstatus = 1 and parent != %s""" % (get_qty, curr_doctype, ref_tab_fname, '%s', '%s'),
|
docstatus = 1 and parent != %s""" % (get_qty, curr_doctype, ref_tab_fname, '%s', '%s'),
|
||||||
(ref_tab_dn, curr_parent_name))
|
(ref_tab_dn, curr_parent_name))
|
||||||
qty = qty and flt(qty[0][0]) or 0
|
qty = qty and flt(qty[0][0]) or 0
|
||||||
|
|
||||||
# get total qty of ref doctype
|
# get total qty of ref doctype
|
||||||
#--------------------
|
#--------------------
|
||||||
max_qty = frappe.db.sql("""select qty from `tab%s` where name = %s
|
max_qty = frappe.db.sql("""select qty from `tab%s` where name = %s
|
||||||
and docstatus = 1""" % (ref_doc_tname, '%s'), ref_tab_dn)
|
and docstatus = 1""" % (ref_doc_tname, '%s'), ref_tab_dn)
|
||||||
max_qty = max_qty and flt(max_qty[0][0]) or 0
|
max_qty = max_qty and flt(max_qty[0][0]) or 0
|
||||||
|
|
||||||
return cstr(qty)+'~~~'+cstr(max_qty)
|
return cstr(qty)+'~~~'+cstr(max_qty)
|
||||||
|
|
||||||
def check_for_stopped_status(self, doctype, docname):
|
def check_for_stopped_status(self, doctype, docname):
|
||||||
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
||||||
status = 'Stopped'""" % (doctype, '%s'), docname)
|
status = 'Stopped'""" % (doctype, '%s'), docname)
|
||||||
if stopped:
|
if stopped:
|
||||||
frappe.throw("One cannot do any transaction against %s : %s, it's status is 'Stopped'" %
|
frappe.throw("One cannot do any transaction against %s : %s, it's status is 'Stopped'" %
|
||||||
(doctype, docname))
|
(doctype, docname), exc=frappe.InvalidStatusError)
|
||||||
|
|
||||||
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
|
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
|
||||||
if check == 'Next':
|
if check == 'Next':
|
||||||
submitted = frappe.db.sql("""select t1.name from `tab%s` t1,`tab%s` t2
|
submitted = frappe.db.sql("""select t1.name from `tab%s` t1,`tab%s` t2
|
||||||
where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1"""
|
where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1"""
|
||||||
% (doctype, detail_doctype, '%s'), docname)
|
% (doctype, detail_doctype, '%s'), docname)
|
||||||
if submitted:
|
if submitted:
|
||||||
frappe.throw(cstr(doctype) + ": " + cstr(submitted[0][0])
|
frappe.throw(cstr(doctype) + ": " + cstr(submitted[0][0])
|
||||||
+ _("has already been submitted."))
|
+ _("has already been submitted."))
|
||||||
|
|
||||||
if check == 'Previous':
|
if check == 'Previous':
|
||||||
submitted = frappe.db.sql("""select name from `tab%s`
|
submitted = frappe.db.sql("""select name from `tab%s`
|
||||||
where docstatus = 1 and name = %s""" % (doctype, '%s'), docname)
|
where docstatus = 1 and name = %s""" % (doctype, '%s'), docname)
|
||||||
if not submitted:
|
if not submitted:
|
||||||
frappe.throw(cstr(doctype) + ": " + cstr(submitted[0][0]) + _("not submitted"))
|
frappe.throw(cstr(doctype) + ": " + cstr(submitted[0][0]) + _("not submitted"))
|
||||||
|
@ -3,27 +3,27 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import cstr, flt
|
from frappe.utils import cstr, flt
|
||||||
|
|
||||||
from frappe import msgprint
|
from frappe import msgprint
|
||||||
|
|
||||||
|
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
|
|
||||||
class PurchaseOrder(BuyingController):
|
class PurchaseOrder(BuyingController):
|
||||||
tname = 'Purchase Order Item'
|
tname = 'Purchase Order Item'
|
||||||
fname = 'po_details'
|
fname = 'po_details'
|
||||||
status_updater = [{
|
|
||||||
'source_dt': 'Purchase Order Item',
|
def __init__(self, arg1, arg2=None):
|
||||||
'target_dt': 'Material Request Item',
|
super(PurchaseOrder, self).__init__(arg1, arg2)
|
||||||
'join_field': 'prevdoc_detail_docname',
|
self.status_updater = [{
|
||||||
'target_field': 'ordered_qty',
|
'source_dt': 'Purchase Order Item',
|
||||||
'target_parent_dt': 'Material Request',
|
'target_dt': 'Material Request Item',
|
||||||
'target_parent_field': 'per_ordered',
|
'join_field': 'prevdoc_detail_docname',
|
||||||
'target_ref_field': 'qty',
|
'target_field': 'ordered_qty',
|
||||||
'source_field': 'qty',
|
'target_parent_dt': 'Material Request',
|
||||||
'percent_join_field': 'prevdoc_docname',
|
'target_parent_field': 'per_ordered',
|
||||||
}]
|
'target_ref_field': 'qty',
|
||||||
|
'source_field': 'qty',
|
||||||
|
'percent_join_field': 'prevdoc_docname',
|
||||||
|
}]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(PurchaseOrder, self).validate()
|
super(PurchaseOrder, self).validate()
|
||||||
@ -156,7 +156,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
frappe.db.set(self,'status','Submitted')
|
frappe.db.set(self,'status','Submitted')
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
pc_obj = frappe.get_doc(dt = 'Purchase Common')
|
pc_obj = frappe.get_doc('Purchase Common')
|
||||||
self.check_for_stopped_status(pc_obj)
|
self.check_for_stopped_status(pc_obj)
|
||||||
|
|
||||||
# Check if Purchase Receipt has been submitted against current Purchase Order
|
# Check if Purchase Receipt has been submitted against current Purchase Order
|
||||||
|
@ -64,11 +64,11 @@ class StatusUpdater(DocListController):
|
|||||||
def update_prevdoc_status(self):
|
def update_prevdoc_status(self):
|
||||||
self.update_qty()
|
self.update_qty()
|
||||||
self.validate_qty()
|
self.validate_qty()
|
||||||
|
|
||||||
def set_status(self, update=False):
|
def set_status(self, update=False):
|
||||||
if self.get("__islocal"):
|
if self.get("__islocal"):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.doctype in status_map:
|
if self.doctype in status_map:
|
||||||
sl = status_map[self.doctype][:]
|
sl = status_map[self.doctype][:]
|
||||||
sl.reverse()
|
sl.reverse()
|
||||||
@ -83,15 +83,15 @@ class StatusUpdater(DocListController):
|
|||||||
elif getattr(self, s[1])():
|
elif getattr(self, s[1])():
|
||||||
self.status = s[0]
|
self.status = s[0]
|
||||||
break
|
break
|
||||||
|
|
||||||
if update:
|
if update:
|
||||||
frappe.db.set_value(self.doctype, self.name, "status", self.status)
|
frappe.db.set_value(self.doctype, self.name, "status", self.status)
|
||||||
|
|
||||||
def on_communication(self):
|
def on_communication(self):
|
||||||
self.communication_set = True
|
self.communication_set = True
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
del self.communication_set
|
del self.communication_set
|
||||||
|
|
||||||
def communication_received(self):
|
def communication_received(self):
|
||||||
if getattr(self, "communication_set", False):
|
if getattr(self, "communication_set", False):
|
||||||
last_comm = self.get("communications")
|
last_comm = self.get("communications")
|
||||||
@ -103,14 +103,14 @@ class StatusUpdater(DocListController):
|
|||||||
last_comm = self.get("communications")
|
last_comm = self.get("communications")
|
||||||
if last_comm:
|
if last_comm:
|
||||||
return last_comm[-1].sent_or_received == "Sent"
|
return last_comm[-1].sent_or_received == "Sent"
|
||||||
|
|
||||||
def validate_qty(self):
|
def validate_qty(self):
|
||||||
"""
|
"""
|
||||||
Validates qty at row level
|
Validates qty at row level
|
||||||
"""
|
"""
|
||||||
self.tolerance = {}
|
self.tolerance = {}
|
||||||
self.global_tolerance = None
|
self.global_tolerance = None
|
||||||
|
|
||||||
for args in self.status_updater:
|
for args in self.status_updater:
|
||||||
# get unique transactions to update
|
# get unique transactions to update
|
||||||
for d in self.get_all_children():
|
for d in self.get_all_children():
|
||||||
@ -118,10 +118,10 @@ class StatusUpdater(DocListController):
|
|||||||
args['name'] = d.get(args['join_field'])
|
args['name'] = d.get(args['join_field'])
|
||||||
|
|
||||||
# get all qty where qty > target_field
|
# get all qty where qty > target_field
|
||||||
item = frappe.db.sql("""select item_code, `{target_ref_field}`,
|
item = frappe.db.sql("""select item_code, `{target_ref_field}`,
|
||||||
`{target_field}`, parenttype, parent from `tab{target_dt}`
|
`{target_field}`, parenttype, parent from `tab{target_dt}`
|
||||||
where `{target_ref_field}` < `{target_field}`
|
where `{target_ref_field}` < `{target_field}`
|
||||||
and name=%s and docstatus=1""".format(**args),
|
and name=%s and docstatus=1""".format(**args),
|
||||||
args['name'], as_dict=1)
|
args['name'], as_dict=1)
|
||||||
if item:
|
if item:
|
||||||
item = item[0]
|
item = item[0]
|
||||||
@ -142,38 +142,37 @@ class StatusUpdater(DocListController):
|
|||||||
is <b>""" % item + cstr(item[args['target_ref_field']]) +
|
is <b>""" % item + cstr(item[args['target_ref_field']]) +
|
||||||
"""</b>.<br>You must reduce the %(target_ref_field)s by \
|
"""</b>.<br>You must reduce the %(target_ref_field)s by \
|
||||||
%(reduce_by)s""" % item, raise_exception=1)
|
%(reduce_by)s""" % item, raise_exception=1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.check_overflow_with_tolerance(item, args)
|
self.check_overflow_with_tolerance(item, args)
|
||||||
|
|
||||||
def check_overflow_with_tolerance(self, item, args):
|
def check_overflow_with_tolerance(self, item, args):
|
||||||
"""
|
"""
|
||||||
Checks if there is overflow condering a relaxation tolerance
|
Checks if there is overflow condering a relaxation tolerance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check if overflow is within tolerance
|
# check if overflow is within tolerance
|
||||||
tolerance, self.tolerance, self.global_tolerance = get_tolerance_for(item['item_code'],
|
tolerance, self.tolerance, self.global_tolerance = get_tolerance_for(item['item_code'],
|
||||||
self.tolerance, self.global_tolerance)
|
self.tolerance, self.global_tolerance)
|
||||||
|
|
||||||
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
|
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
|
||||||
item[args['target_ref_field']]) * 100
|
item[args['target_ref_field']]) * 100
|
||||||
|
|
||||||
if overflow_percent - tolerance > 0.01:
|
if overflow_percent - tolerance > 0.01:
|
||||||
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
|
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
|
||||||
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
|
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
|
||||||
|
|
||||||
msgprint("""
|
msgprint("""
|
||||||
Row #%(idx)s: Max %(target_ref_field)s allowed for <b>Item %(item_code)s</b> \
|
Row #%(idx)s: Max %(target_ref_field)s allowed for <b>Item %(item_code)s</b> \
|
||||||
against <b>%(parenttype)s %(parent)s</b> is <b>%(max_allowed)s</b>.
|
against <b>%(parenttype)s %(parent)s</b> is <b>%(max_allowed)s</b>.
|
||||||
|
|
||||||
If you want to increase your overflow tolerance, please increase tolerance %% in \
|
If you want to increase your overflow tolerance, please increase tolerance %% in \
|
||||||
Global Defaults or Item master.
|
Global Defaults or Item master.
|
||||||
|
|
||||||
Or, you must reduce the %(target_ref_field)s by %(reduce_by)s
|
Or, you must reduce the %(target_ref_field)s by %(reduce_by)s
|
||||||
|
|
||||||
Also, please check if the order item has already been billed in the Sales Order""" %
|
Also, please check if the order item has already been billed in the Sales Order""" %
|
||||||
item, raise_exception=1)
|
item, raise_exception=1)
|
||||||
|
|
||||||
|
|
||||||
def update_qty(self, change_modified=True):
|
def update_qty(self, change_modified=True):
|
||||||
"""
|
"""
|
||||||
@ -185,103 +184,103 @@ class StatusUpdater(DocListController):
|
|||||||
args['cond'] = ' or parent="%s"' % self.name.replace('"', '\"')
|
args['cond'] = ' or parent="%s"' % self.name.replace('"', '\"')
|
||||||
else:
|
else:
|
||||||
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
|
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
|
||||||
|
|
||||||
args['modified_cond'] = ''
|
args['modified_cond'] = ''
|
||||||
if change_modified:
|
if change_modified:
|
||||||
args['modified_cond'] = ', modified = now()'
|
args['modified_cond'] = ', modified = now()'
|
||||||
|
|
||||||
# update quantities in child table
|
# update quantities in child table
|
||||||
for d in self.get_all_children():
|
for d in self.get_all_children():
|
||||||
if d.doctype == args['source_dt']:
|
if d.doctype == args['source_dt']:
|
||||||
# updates qty in the child table
|
# updates qty in the child table
|
||||||
args['detail_id'] = d.get(args['join_field'])
|
args['detail_id'] = d.get(args['join_field'])
|
||||||
|
|
||||||
args['second_source_condition'] = ""
|
args['second_source_condition'] = ""
|
||||||
if args.get('second_source_dt') and args.get('second_source_field') \
|
if args.get('second_source_dt') and args.get('second_source_field') \
|
||||||
and args.get('second_join_field'):
|
and args.get('second_join_field'):
|
||||||
args['second_source_condition'] = """ + (select sum(%(second_source_field)s)
|
args['second_source_condition'] = """ + (select sum(%(second_source_field)s)
|
||||||
from `tab%(second_source_dt)s`
|
from `tab%(second_source_dt)s`
|
||||||
where `%(second_join_field)s`="%(detail_id)s"
|
where `%(second_join_field)s`="%(detail_id)s"
|
||||||
and (docstatus=1))""" % args
|
and (docstatus=1))""" % args
|
||||||
|
|
||||||
if args['detail_id']:
|
if args['detail_id']:
|
||||||
frappe.db.sql("""update `tab%(target_dt)s`
|
frappe.db.sql("""update `tab%(target_dt)s`
|
||||||
set %(target_field)s = (select sum(%(source_field)s)
|
set %(target_field)s = (select sum(%(source_field)s)
|
||||||
from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
|
from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
|
||||||
and (docstatus=1 %(cond)s)) %(second_source_condition)s
|
and (docstatus=1 %(cond)s)) %(second_source_condition)s
|
||||||
where name='%(detail_id)s'""" % args)
|
where name='%(detail_id)s'""" % args)
|
||||||
|
|
||||||
# get unique transactions to update
|
# get unique transactions to update
|
||||||
for name in set([d.get(args['percent_join_field']) for d in self.get_all_children(args['source_dt'])]):
|
for name in set([d.get(args['percent_join_field']) for d in self.get_all_children(args['source_dt'])]):
|
||||||
if name:
|
if name:
|
||||||
args['name'] = name
|
args['name'] = name
|
||||||
|
|
||||||
# update percent complete in the parent table
|
# update percent complete in the parent table
|
||||||
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
||||||
set %(target_parent_field)s = (select sum(if(%(target_ref_field)s >
|
set %(target_parent_field)s = (select sum(if(%(target_ref_field)s >
|
||||||
ifnull(%(target_field)s, 0), %(target_field)s,
|
ifnull(%(target_field)s, 0), %(target_field)s,
|
||||||
%(target_ref_field)s))/sum(%(target_ref_field)s)*100
|
%(target_ref_field)s))/sum(%(target_ref_field)s)*100
|
||||||
from `tab%(target_dt)s` where parent="%(name)s") %(modified_cond)s
|
from `tab%(target_dt)s` where parent="%(name)s") %(modified_cond)s
|
||||||
where name='%(name)s'""" % args)
|
where name='%(name)s'""" % args)
|
||||||
|
|
||||||
# update field
|
# update field
|
||||||
if args.get('status_field'):
|
if args.get('status_field'):
|
||||||
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
||||||
set %(status_field)s = if(ifnull(%(target_parent_field)s,0)<0.001,
|
set %(status_field)s = if(ifnull(%(target_parent_field)s,0)<0.001,
|
||||||
'Not %(keyword)s', if(%(target_parent_field)s>=99.99,
|
'Not %(keyword)s', if(%(target_parent_field)s>=99.99,
|
||||||
'Fully %(keyword)s', 'Partly %(keyword)s'))
|
'Fully %(keyword)s', 'Partly %(keyword)s'))
|
||||||
where name='%(name)s'""" % args)
|
where name='%(name)s'""" % args)
|
||||||
|
|
||||||
|
|
||||||
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
|
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
|
||||||
ref_fieldname = ref_dt.lower().replace(" ", "_")
|
ref_fieldname = ref_dt.lower().replace(" ", "_")
|
||||||
zero_amount_refdoc = []
|
zero_amount_refdoc = []
|
||||||
all_zero_amount_refdoc = frappe.db.sql_list("""select name from `tab%s`
|
all_zero_amount_refdoc = frappe.db.sql_list("""select name from `tab%s`
|
||||||
where docstatus=1 and net_total = 0""" % ref_dt)
|
where docstatus=1 and net_total = 0""" % ref_dt)
|
||||||
|
|
||||||
for item in self.get("entries"):
|
for item in self.get("entries"):
|
||||||
if item.get(ref_fieldname) \
|
if item.get(ref_fieldname) \
|
||||||
and item.get(ref_fieldname) in all_zero_amount_refdoc \
|
and item.get(ref_fieldname) in all_zero_amount_refdoc \
|
||||||
and item.get(ref_fieldname) not in zero_amount_refdoc:
|
and item.get(ref_fieldname) not in zero_amount_refdoc:
|
||||||
zero_amount_refdoc.append(item.get(ref_fieldname))
|
zero_amount_refdoc.append(item.get(ref_fieldname))
|
||||||
|
|
||||||
if zero_amount_refdoc:
|
if zero_amount_refdoc:
|
||||||
self.update_biling_status(zero_amount_refdoc, ref_dt, ref_fieldname)
|
self.update_biling_status(zero_amount_refdoc, ref_dt, ref_fieldname)
|
||||||
|
|
||||||
def update_biling_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
|
def update_biling_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
|
||||||
for ref_dn in zero_amount_refdoc:
|
for ref_dn in zero_amount_refdoc:
|
||||||
ref_doc_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0)) from `tab%s Item`
|
ref_doc_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0)) from `tab%s Item`
|
||||||
where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0])
|
where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0])
|
||||||
|
|
||||||
billed_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0))
|
billed_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0))
|
||||||
from `tab%s Item` where %s=%s and docstatus=1""" %
|
from `tab%s Item` where %s=%s and docstatus=1""" %
|
||||||
(self.doctype, ref_fieldname, '%s'), (ref_dn))[0][0])
|
(self.doctype, ref_fieldname, '%s'), (ref_dn))[0][0])
|
||||||
|
|
||||||
per_billed = ((ref_doc_qty if billed_qty > ref_doc_qty else billed_qty)\
|
per_billed = ((ref_doc_qty if billed_qty > ref_doc_qty else billed_qty)\
|
||||||
/ ref_doc_qty)*100
|
/ ref_doc_qty)*100
|
||||||
frappe.db.set_value(ref_dt, ref_dn, "per_billed", per_billed)
|
frappe.db.set_value(ref_dt, ref_dn, "per_billed", per_billed)
|
||||||
|
|
||||||
if frappe.get_meta(ref_dt).get_field("billing_status"):
|
if frappe.get_meta(ref_dt).get_field("billing_status"):
|
||||||
if per_billed < 0.001: billing_status = "Not Billed"
|
if per_billed < 0.001: billing_status = "Not Billed"
|
||||||
elif per_billed >= 99.99: billing_status = "Fully Billed"
|
elif per_billed >= 99.99: billing_status = "Fully Billed"
|
||||||
else: billing_status = "Partly Billed"
|
else: billing_status = "Partly Billed"
|
||||||
|
|
||||||
frappe.db.set_value(ref_dt, ref_dn, "billing_status", billing_status)
|
frappe.db.set_value(ref_dt, ref_dn, "billing_status", billing_status)
|
||||||
|
|
||||||
def get_tolerance_for(item_code, item_tolerance={}, global_tolerance=None):
|
def get_tolerance_for(item_code, item_tolerance={}, global_tolerance=None):
|
||||||
"""
|
"""
|
||||||
Returns the tolerance for the item, if not set, returns global tolerance
|
Returns the tolerance for the item, if not set, returns global tolerance
|
||||||
"""
|
"""
|
||||||
if item_tolerance.get(item_code):
|
if item_tolerance.get(item_code):
|
||||||
return item_tolerance[item_code], item_tolerance, global_tolerance
|
return item_tolerance[item_code], item_tolerance, global_tolerance
|
||||||
|
|
||||||
tolerance = flt(frappe.db.get_value('Item',item_code,'tolerance') or 0)
|
tolerance = flt(frappe.db.get_value('Item',item_code,'tolerance') or 0)
|
||||||
|
|
||||||
if not tolerance:
|
if not tolerance:
|
||||||
if global_tolerance == None:
|
if global_tolerance == None:
|
||||||
global_tolerance = flt(frappe.db.get_value('Global Defaults', None,
|
global_tolerance = flt(frappe.db.get_value('Global Defaults', None,
|
||||||
'tolerance'))
|
'tolerance'))
|
||||||
tolerance = global_tolerance
|
tolerance = global_tolerance
|
||||||
|
|
||||||
item_tolerance[item_code] = tolerance
|
item_tolerance[item_code] = tolerance
|
||||||
return tolerance, item_tolerance, global_tolerance
|
return tolerance, item_tolerance, global_tolerance
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
# ERPNext - web based ERP (http://erpnext.com)
|
# ERPNext - web based ERP (http://erpnext.com)
|
||||||
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
|
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
@ -22,35 +22,35 @@ feed_dict = {
|
|||||||
# Project
|
# Project
|
||||||
'Project': ['[%(status)s]', '#000080'],
|
'Project': ['[%(status)s]', '#000080'],
|
||||||
'Task': ['[%(status)s] %(subject)s', '#000080'],
|
'Task': ['[%(status)s] %(subject)s', '#000080'],
|
||||||
|
|
||||||
# Sales
|
# Sales
|
||||||
'Lead': ['%(lead_name)s', '#000080'],
|
'Lead': ['%(lead_name)s', '#000080'],
|
||||||
'Quotation': ['[%(status)s] To %(customer_name)s worth %(currency)s %(grand_total_export)s', '#4169E1'],
|
'Quotation': ['[%(status)s] To %(customer_name)s worth %(currency)s %(grand_total_export)s', '#4169E1'],
|
||||||
'Sales Order': ['[%(status)s] To %(customer_name)s worth %(currency)s %(grand_total_export)s', '#4169E1'],
|
'Sales Order': ['[%(status)s] To %(customer_name)s worth %(currency)s %(grand_total_export)s', '#4169E1'],
|
||||||
|
|
||||||
# Purchase
|
# Purchase
|
||||||
'Supplier': ['%(supplier_name)s, %(supplier_type)s', '#6495ED'],
|
'Supplier': ['%(supplier_name)s, %(supplier_type)s', '#6495ED'],
|
||||||
'Purchase Order': ['[%(status)s] %(name)s To %(supplier_name)s for %(currency)s %(grand_total_import)s', '#4169E1'],
|
'Purchase Order': ['[%(status)s] %(name)s To %(supplier_name)s for %(currency)s %(grand_total_import)s', '#4169E1'],
|
||||||
|
|
||||||
# Stock
|
# Stock
|
||||||
'Delivery Note': ['[%(status)s] To %(customer_name)s', '#4169E1'],
|
'Delivery Note': ['[%(status)s] To %(customer_name)s', '#4169E1'],
|
||||||
'Purchase Receipt': ['[%(status)s] From %(supplier)s', '#4169E1'],
|
'Purchase Receipt': ['[%(status)s] From %(supplier)s', '#4169E1'],
|
||||||
|
|
||||||
# Accounts
|
# Accounts
|
||||||
'Journal Voucher': ['[%(voucher_type)s] %(name)s', '#4169E1'],
|
'Journal Voucher': ['[%(voucher_type)s] %(name)s', '#4169E1'],
|
||||||
'Purchase Invoice': ['To %(supplier_name)s for %(currency)s %(grand_total_import)s', '#4169E1'],
|
'Purchase Invoice': ['To %(supplier_name)s for %(currency)s %(grand_total_import)s', '#4169E1'],
|
||||||
'Sales Invoice': ['To %(customer_name)s for %(currency)s %(grand_total_export)s', '#4169E1'],
|
'Sales Invoice': ['To %(customer_name)s for %(currency)s %(grand_total_export)s', '#4169E1'],
|
||||||
|
|
||||||
# HR
|
# HR
|
||||||
'Expense Claim': ['[%(approval_status)s] %(name)s by %(employee_name)s', '#4169E1'],
|
'Expense Claim': ['[%(approval_status)s] %(name)s by %(employee_name)s', '#4169E1'],
|
||||||
'Salary Slip': ['%(employee_name)s for %(month)s %(fiscal_year)s', '#4169E1'],
|
'Salary Slip': ['%(employee_name)s for %(month)s %(fiscal_year)s', '#4169E1'],
|
||||||
'Leave Transaction': ['%(leave_type)s for %(employee)s', '#4169E1'],
|
'Leave Transaction': ['%(leave_type)s for %(employee)s', '#4169E1'],
|
||||||
|
|
||||||
# Support
|
# Support
|
||||||
'Customer Issue': ['[%(status)s] %(description)s by %(customer_name)s', '#000080'],
|
'Customer Issue': ['[%(status)s] %(description)s by %(customer_name)s', '#000080'],
|
||||||
'Maintenance Visit': ['To %(customer_name)s', '#4169E1'],
|
'Maintenance Visit': ['To %(customer_name)s', '#4169E1'],
|
||||||
'Support Ticket': ["[%(status)s] %(subject)s", '#000080'],
|
'Support Ticket': ["[%(status)s] %(subject)s", '#000080'],
|
||||||
|
|
||||||
# Website
|
# Website
|
||||||
'Web Page': ['%(title)s', '#000080'],
|
'Web Page': ['%(title)s', '#000080'],
|
||||||
'Blog': ['%(title)s', '#000080']
|
'Blog': ['%(title)s', '#000080']
|
||||||
@ -63,12 +63,12 @@ def make_feed(feedtype, doctype, name, owner, subject, color):
|
|||||||
|
|
||||||
if feedtype in ('Login', 'Comment', 'Assignment'):
|
if feedtype in ('Login', 'Comment', 'Assignment'):
|
||||||
# delete old login, comment feed
|
# delete old login, comment feed
|
||||||
frappe.db.sql("""delete from tabFeed where
|
frappe.db.sql("""delete from tabFeed where
|
||||||
datediff(curdate(), creation) > 7 and doc_type in ('Comment', 'Login', 'Assignment')""")
|
datediff(curdate(), creation) > 7 and doc_type in ('Comment', 'Login', 'Assignment')""")
|
||||||
else:
|
else:
|
||||||
# one feed per item
|
# one feed per item
|
||||||
frappe.db.sql("""delete from tabFeed
|
frappe.db.sql("""delete from tabFeed
|
||||||
where doc_type=%s and doc_name=%s
|
where doc_type=%s and doc_name=%s
|
||||||
and ifnull(feed_type,'') != 'Comment'""", (doctype, name))
|
and ifnull(feed_type,'') != 'Comment'""", (doctype, name))
|
||||||
|
|
||||||
f = frappe.new_doc('Feed')
|
f = frappe.new_doc('Feed')
|
||||||
@ -79,9 +79,9 @@ def make_feed(feedtype, doctype, name, owner, subject, color):
|
|||||||
f.subject = subject
|
f.subject = subject
|
||||||
f.color = color
|
f.color = color
|
||||||
f.full_name = get_fullname(owner)
|
f.full_name = get_fullname(owner)
|
||||||
f.save()
|
f.save(ignore_permissions=True)
|
||||||
|
|
||||||
def update_feed(doc, method=None):
|
def update_feed(doc, method=None):
|
||||||
"adds a new feed"
|
"adds a new feed"
|
||||||
if method in ['on_update', 'on_submit']:
|
if method in ['on_update', 'on_submit']:
|
||||||
subject, color = feed_dict.get(doc.doctype, [None, None])
|
subject, color = feed_dict.get(doc.doctype, [None, None])
|
||||||
|
@ -7,70 +7,73 @@ import frappe
|
|||||||
from frappe.utils import cstr, getdate
|
from frappe.utils import cstr, getdate
|
||||||
|
|
||||||
from frappe import msgprint
|
from frappe import msgprint
|
||||||
from erpnext.stock.utils import get_valid_serial_nos
|
from erpnext.stock.utils import get_valid_serial_nos
|
||||||
|
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
class InstallationNote(TransactionBase):
|
class InstallationNote(TransactionBase):
|
||||||
tname = 'Installation Note Item'
|
tname = 'Installation Note Item'
|
||||||
fname = 'installed_item_details'
|
fname = 'installed_item_details'
|
||||||
status_updater = [{
|
|
||||||
'source_dt': 'Installation Note Item',
|
def __init__(self, arg1, arg2=None):
|
||||||
'target_dt': 'Delivery Note Item',
|
super(InstallationNote, self).__init__(arg1, arg2)
|
||||||
'target_field': 'installed_qty',
|
self.status_updater = [{
|
||||||
'target_ref_field': 'qty',
|
'source_dt': 'Installation Note Item',
|
||||||
'join_field': 'prevdoc_detail_docname',
|
'target_dt': 'Delivery Note Item',
|
||||||
'target_parent_dt': 'Delivery Note',
|
'target_field': 'installed_qty',
|
||||||
'target_parent_field': 'per_installed',
|
'target_ref_field': 'qty',
|
||||||
'source_field': 'qty',
|
'join_field': 'prevdoc_detail_docname',
|
||||||
'percent_join_field': 'prevdoc_docname',
|
'target_parent_dt': 'Delivery Note',
|
||||||
'status_field': 'installation_status',
|
'target_parent_field': 'per_installed',
|
||||||
'keyword': 'Installed'
|
'source_field': 'qty',
|
||||||
}]
|
'percent_join_field': 'prevdoc_docname',
|
||||||
|
'status_field': 'installation_status',
|
||||||
|
'keyword': 'Installed'
|
||||||
|
}]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_fiscal_year()
|
self.validate_fiscal_year()
|
||||||
self.validate_installation_date()
|
self.validate_installation_date()
|
||||||
self.check_item_table()
|
self.check_item_table()
|
||||||
|
|
||||||
from erpnext.controllers.selling_controller import check_active_sales_items
|
from erpnext.controllers.selling_controller import check_active_sales_items
|
||||||
check_active_sales_items(self)
|
check_active_sales_items(self)
|
||||||
|
|
||||||
def validate_fiscal_year(self):
|
def validate_fiscal_year(self):
|
||||||
from erpnext.accounts.utils import validate_fiscal_year
|
from erpnext.accounts.utils import validate_fiscal_year
|
||||||
validate_fiscal_year(self.inst_date, self.fiscal_year, "Installation Date")
|
validate_fiscal_year(self.inst_date, self.fiscal_year, "Installation Date")
|
||||||
|
|
||||||
def is_serial_no_added(self, item_code, serial_no):
|
def is_serial_no_added(self, item_code, serial_no):
|
||||||
ar_required = frappe.db.get_value("Item", item_code, "has_serial_no")
|
ar_required = frappe.db.get_value("Item", item_code, "has_serial_no")
|
||||||
if ar_required == 'Yes' and not serial_no:
|
if ar_required == 'Yes' and not serial_no:
|
||||||
msgprint("Serial No is mandatory for item: " + item_code, raise_exception=1)
|
msgprint("Serial No is mandatory for item: " + item_code, raise_exception=1)
|
||||||
elif ar_required != 'Yes' and cstr(serial_no).strip():
|
elif ar_required != 'Yes' and cstr(serial_no).strip():
|
||||||
msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :" +
|
msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :" +
|
||||||
item_code, raise_exception=1)
|
item_code, raise_exception=1)
|
||||||
|
|
||||||
def is_serial_no_exist(self, item_code, serial_no):
|
def is_serial_no_exist(self, item_code, serial_no):
|
||||||
for x in serial_no:
|
for x in serial_no:
|
||||||
if not frappe.db.exists("Serial No", x):
|
if not frappe.db.exists("Serial No", x):
|
||||||
msgprint("Serial No " + x + " does not exist in the system", raise_exception=1)
|
msgprint("Serial No " + x + " does not exist in the system", raise_exception=1)
|
||||||
|
|
||||||
def is_serial_no_installed(self,cur_s_no,item_code):
|
def is_serial_no_installed(self,cur_s_no,item_code):
|
||||||
for x in cur_s_no:
|
for x in cur_s_no:
|
||||||
status = frappe.db.sql("select status from `tabSerial No` where name = %s", x)
|
status = frappe.db.sql("select status from `tabSerial No` where name = %s", x)
|
||||||
status = status and status[0][0] or ''
|
status = status and status[0][0] or ''
|
||||||
|
|
||||||
if status == 'Installed':
|
if status == 'Installed':
|
||||||
msgprint("Item "+item_code+" with serial no. " + x + " already installed",
|
msgprint("Item "+item_code+" with serial no. " + x + " already installed",
|
||||||
raise_exception=1)
|
raise_exception=1)
|
||||||
|
|
||||||
def get_prevdoc_serial_no(self, prevdoc_detail_docname):
|
def get_prevdoc_serial_no(self, prevdoc_detail_docname):
|
||||||
serial_nos = frappe.db.get_value("Delivery Note Item",
|
serial_nos = frappe.db.get_value("Delivery Note Item",
|
||||||
prevdoc_detail_docname, "serial_no")
|
prevdoc_detail_docname, "serial_no")
|
||||||
return get_valid_serial_nos(serial_nos)
|
return get_valid_serial_nos(serial_nos)
|
||||||
|
|
||||||
def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname):
|
def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname):
|
||||||
for sr in cur_s_no:
|
for sr in cur_s_no:
|
||||||
if sr not in prevdoc_s_no:
|
if sr not in prevdoc_s_no:
|
||||||
msgprint("Serial No. " + sr + " is not matching with the Delivery Note " +
|
msgprint("Serial No. " + sr + " is not matching with the Delivery Note " +
|
||||||
prevdoc_docname, raise_exception = 1)
|
prevdoc_docname, raise_exception = 1)
|
||||||
|
|
||||||
def validate_serial_no(self):
|
def validate_serial_no(self):
|
||||||
@ -80,33 +83,33 @@ class InstallationNote(TransactionBase):
|
|||||||
if d.serial_no:
|
if d.serial_no:
|
||||||
sr_list = get_valid_serial_nos(d.serial_no, d.qty, d.item_code)
|
sr_list = get_valid_serial_nos(d.serial_no, d.qty, d.item_code)
|
||||||
self.is_serial_no_exist(d.item_code, sr_list)
|
self.is_serial_no_exist(d.item_code, sr_list)
|
||||||
|
|
||||||
prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname)
|
prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname)
|
||||||
if prevdoc_s_no:
|
if prevdoc_s_no:
|
||||||
self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname)
|
self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname)
|
||||||
|
|
||||||
self.is_serial_no_installed(sr_list, d.item_code)
|
self.is_serial_no_installed(sr_list, d.item_code)
|
||||||
|
|
||||||
def validate_installation_date(self):
|
def validate_installation_date(self):
|
||||||
for d in self.get('installed_item_details'):
|
for d in self.get('installed_item_details'):
|
||||||
if d.prevdoc_docname:
|
if d.prevdoc_docname:
|
||||||
d_date = frappe.db.get_value("Delivery Note", d.prevdoc_docname, "posting_date")
|
d_date = frappe.db.get_value("Delivery Note", d.prevdoc_docname, "posting_date")
|
||||||
if d_date > getdate(self.inst_date):
|
if d_date > getdate(self.inst_date):
|
||||||
msgprint("Installation Date can not be before Delivery Date " + cstr(d_date) +
|
msgprint("Installation Date can not be before Delivery Date " + cstr(d_date) +
|
||||||
" for item "+d.item_code, raise_exception=1)
|
" for item "+d.item_code, raise_exception=1)
|
||||||
|
|
||||||
def check_item_table(self):
|
def check_item_table(self):
|
||||||
if not(self.get('installed_item_details')):
|
if not(self.get('installed_item_details')):
|
||||||
msgprint("Please fetch items from Delivery Note selected", raise_exception=1)
|
msgprint("Please fetch items from Delivery Note selected", raise_exception=1)
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
frappe.db.set(self, 'status', 'Draft')
|
frappe.db.set(self, 'status', 'Draft')
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.validate_serial_no()
|
self.validate_serial_no()
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
for d in self.get('installed_item_details'):
|
for d in self.get('installed_item_details'):
|
||||||
if d.serial_no:
|
if d.serial_no:
|
||||||
|
@ -21,9 +21,9 @@ class TestQuotation(unittest.TestCase):
|
|||||||
sales_order = make_sales_order(quotation.name)
|
sales_order = make_sales_order(quotation.name)
|
||||||
|
|
||||||
self.assertEquals(sales_order.doctype, "Sales Order")
|
self.assertEquals(sales_order.doctype, "Sales Order")
|
||||||
self.assertEquals(len(sales_order.get("sales_order_details")), 2)
|
self.assertEquals(len(sales_order.get("sales_order_details")), 1)
|
||||||
self.assertEquals(sales_order.get("sales_order_details")[0]["doctype"], "Sales Order Item")
|
self.assertEquals(sales_order.get("sales_order_details")[0].doctype, "Sales Order Item")
|
||||||
self.assertEquals(sales_order.get("sales_order_details")[0]["prevdoc_docname"], quotation.name)
|
self.assertEquals(sales_order.get("sales_order_details")[0].prevdoc_docname, quotation.name)
|
||||||
self.assertEquals(sales_order.customer, "_Test Customer")
|
self.assertEquals(sales_order.customer, "_Test Customer")
|
||||||
|
|
||||||
sales_order.delivery_date = "2014-01-01"
|
sales_order.delivery_date = "2014-01-01"
|
||||||
|
@ -281,10 +281,10 @@ def make_material_request(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_delivery_note(source_name, target_doc=None):
|
def make_delivery_note(source_name, target_doc=None):
|
||||||
def update_item(obj, target, source_parent):
|
def update_item(source, target, source_parent):
|
||||||
target.base_amount = (flt(obj.qty) - flt(obj.delivered_qty)) * flt(obj.base_rate)
|
target.base_amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.base_rate)
|
||||||
target.amount = (flt(obj.qty) - flt(obj.delivered_qty)) * flt(obj.rate)
|
target.amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.rate)
|
||||||
target.qty = flt(obj.qty) - flt(obj.delivered_qty)
|
target.qty = flt(source.qty) - flt(source.delivered_qty)
|
||||||
|
|
||||||
doclist = get_mapped_doc("Sales Order", source_name, {
|
doclist = get_mapped_doc("Sales Order", source_name, {
|
||||||
"Sales Order": {
|
"Sales Order": {
|
||||||
@ -326,10 +326,10 @@ def make_sales_invoice(source_name, target_doc=None):
|
|||||||
doc.is_pos = 0
|
doc.is_pos = 0
|
||||||
doc.run_method("onload_post_render")
|
doc.run_method("onload_post_render")
|
||||||
|
|
||||||
def update_item(obj, target, source_parent):
|
def update_item(source, target, source_parent):
|
||||||
target.amount = flt(obj.amount) - flt(obj.billed_amt)
|
target.amount = flt(source.amount) - flt(source.billed_amt)
|
||||||
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
||||||
target.qty = obj.rate and target.amount / flt(obj.rate) or obj.qty
|
target.qty = source.rate and target.amount / flt(source.rate) or obj.qty
|
||||||
|
|
||||||
doclist = get_mapped_doc("Sales Order", source_name, {
|
doclist = get_mapped_doc("Sales Order", source_name, {
|
||||||
"Sales Order": {
|
"Sales Order": {
|
||||||
|
@ -106,7 +106,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
def test_reserved_qty_for_so(self):
|
def test_reserved_qty_for_so(self):
|
||||||
# reset bin
|
# reset bin
|
||||||
so_item = test_records[0]["sales_order_details"][0]
|
so_item = test_records[0]["sales_order_details"][0].copy()
|
||||||
self.delete_bin(so_item["item_code"], so_item["warehouse"])
|
self.delete_bin(so_item["item_code"], so_item["warehouse"])
|
||||||
|
|
||||||
# submit
|
# submit
|
||||||
@ -120,7 +120,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
def test_reserved_qty_for_partial_delivery(self):
|
def test_reserved_qty_for_partial_delivery(self):
|
||||||
# reset bin
|
# reset bin
|
||||||
so_item = test_records[0]["sales_order_details"][0]
|
so_item = test_records[0]["sales_order_details"][0].copy()
|
||||||
self.delete_bin(so_item["item_code"], so_item["warehouse"])
|
self.delete_bin(so_item["item_code"], so_item["warehouse"])
|
||||||
|
|
||||||
# submit so
|
# submit so
|
||||||
@ -150,7 +150,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
def test_reserved_qty_for_over_delivery(self):
|
def test_reserved_qty_for_over_delivery(self):
|
||||||
# reset bin
|
# reset bin
|
||||||
so_item = test_records[0]["sales_order_details"][0]
|
so_item = test_records[0]["sales_order_details"][0].copy()
|
||||||
self.delete_bin(so_item["item_code"], so_item["warehouse"])
|
self.delete_bin(so_item["item_code"], so_item["warehouse"])
|
||||||
|
|
||||||
# submit so
|
# submit so
|
||||||
|
@ -5,22 +5,33 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
|
|
||||||
class ItemGroup(NestedSet):
|
class ItemGroup(NestedSet, WebsiteGenerator):
|
||||||
nsm_parent_field = 'parent_item_group'
|
nsm_parent_field = 'parent_item_group'
|
||||||
|
|
||||||
|
def autoname(self):
|
||||||
|
self.name = self.item_group_name
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.parent_website_route:
|
if not self.parent_website_route:
|
||||||
self.parent_website_route = frappe.get_website_route("Item Group",
|
self.parent_website_route = frappe.get_website_route("Item Group",
|
||||||
self.parent_item_group)
|
self.parent_item_group)
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
NestedSet.on_update(self)
|
NestedSet.on_update(self)
|
||||||
|
WebsiteGenerator.on_update(self)
|
||||||
self.validate_name_with_item()
|
self.validate_name_with_item()
|
||||||
|
|
||||||
self.validate_one_root()
|
self.validate_one_root()
|
||||||
|
|
||||||
|
def after_rename(self, olddn, newdn, merge=False):
|
||||||
|
NestedSet.after_rename(self, olddn, newdn, merge)
|
||||||
|
WebsiteGenerator.after_rename(self, olddn, newdn, merge)
|
||||||
|
|
||||||
|
def on_trash(self):
|
||||||
|
NestedSet.on_trash(self)
|
||||||
|
WebsiteGenerator.on_trash(self)
|
||||||
|
|
||||||
def validate_name_with_item(self):
|
def validate_name_with_item(self):
|
||||||
if frappe.db.exists("Item", self.name):
|
if frappe.db.exists("Item", self.name):
|
||||||
frappe.msgprint("An item exists with same name (%s), please change the \
|
frappe.msgprint("An item exists with same name (%s), please change the \
|
||||||
|
@ -13,15 +13,14 @@ class TestItem(unittest.TestCase):
|
|||||||
def test_basic_tree(self, records=None):
|
def test_basic_tree(self, records=None):
|
||||||
min_lft = 1
|
min_lft = 1
|
||||||
max_rgt = frappe.db.sql("select max(rgt) from `tabItem Group`")[0][0]
|
max_rgt = frappe.db.sql("select max(rgt) from `tabItem Group`")[0][0]
|
||||||
|
|
||||||
if not records:
|
if not records:
|
||||||
records = test_records[2:]
|
records = test_records[2:]
|
||||||
|
|
||||||
for item_group in records:
|
for item_group in records:
|
||||||
item_group = item_group[0]
|
lft, rgt, parent_item_group = frappe.db.get_value("Item Group", item_group["item_group_name"],
|
||||||
lft, rgt, parent_item_group = frappe.db.get_value("Item Group", item_group["item_group_name"],
|
|
||||||
["lft", "rgt", "parent_item_group"])
|
["lft", "rgt", "parent_item_group"])
|
||||||
|
|
||||||
if parent_item_group:
|
if parent_item_group:
|
||||||
parent_lft, parent_rgt = frappe.db.get_value("Item Group", parent_item_group,
|
parent_lft, parent_rgt = frappe.db.get_value("Item Group", parent_item_group,
|
||||||
["lft", "rgt"])
|
["lft", "rgt"])
|
||||||
@ -29,7 +28,7 @@ class TestItem(unittest.TestCase):
|
|||||||
# root
|
# root
|
||||||
parent_lft = min_lft - 1
|
parent_lft = min_lft - 1
|
||||||
parent_rgt = max_rgt + 1
|
parent_rgt = max_rgt + 1
|
||||||
|
|
||||||
self.assertTrue(lft)
|
self.assertTrue(lft)
|
||||||
self.assertTrue(rgt)
|
self.assertTrue(rgt)
|
||||||
self.assertTrue(lft < rgt)
|
self.assertTrue(lft < rgt)
|
||||||
@ -38,69 +37,69 @@ class TestItem(unittest.TestCase):
|
|||||||
self.assertTrue(rgt < parent_rgt)
|
self.assertTrue(rgt < parent_rgt)
|
||||||
self.assertTrue(lft >= min_lft)
|
self.assertTrue(lft >= min_lft)
|
||||||
self.assertTrue(rgt <= max_rgt)
|
self.assertTrue(rgt <= max_rgt)
|
||||||
|
|
||||||
no_of_children = self.get_no_of_children(item_group["item_group_name"])
|
no_of_children = self.get_no_of_children(item_group["item_group_name"])
|
||||||
self.assertTrue(rgt == (lft + 1 + (2 * no_of_children)))
|
self.assertTrue(rgt == (lft + 1 + (2 * no_of_children)))
|
||||||
|
|
||||||
no_of_children = self.get_no_of_children(parent_item_group)
|
no_of_children = self.get_no_of_children(parent_item_group)
|
||||||
self.assertTrue(parent_rgt == (parent_lft + 1 + (2 * no_of_children)))
|
self.assertTrue(parent_rgt == (parent_lft + 1 + (2 * no_of_children)))
|
||||||
|
|
||||||
def get_no_of_children(self, item_group):
|
def get_no_of_children(self, item_group):
|
||||||
def get_no_of_children(item_groups, no_of_children):
|
def get_no_of_children(item_groups, no_of_children):
|
||||||
children = []
|
children = []
|
||||||
for ig in item_groups:
|
for ig in item_groups:
|
||||||
children += frappe.db.sql_list("""select name from `tabItem Group`
|
children += frappe.db.sql_list("""select name from `tabItem Group`
|
||||||
where ifnull(parent_item_group, '')=%s""", ig or '')
|
where ifnull(parent_item_group, '')=%s""", ig or '')
|
||||||
|
|
||||||
if len(children):
|
if len(children):
|
||||||
return get_no_of_children(children, no_of_children + len(children))
|
return get_no_of_children(children, no_of_children + len(children))
|
||||||
else:
|
else:
|
||||||
return no_of_children
|
return no_of_children
|
||||||
|
|
||||||
return get_no_of_children([item_group], 0)
|
return get_no_of_children([item_group], 0)
|
||||||
|
|
||||||
def test_recursion(self):
|
def test_recursion(self):
|
||||||
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
||||||
group_b.parent_item_group = "_Test Item Group B - 3"
|
group_b.parent_item_group = "_Test Item Group B - 3"
|
||||||
self.assertRaises(NestedSetRecursionError, group_b.save)
|
self.assertRaises(NestedSetRecursionError, group_b.save)
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
group_b.parent_item_group = "All Item Groups"
|
group_b.parent_item_group = "All Item Groups"
|
||||||
group_b.save()
|
group_b.save()
|
||||||
|
|
||||||
def test_rebuild_tree(self):
|
def test_rebuild_tree(self):
|
||||||
rebuild_tree("Item Group", "parent_item_group")
|
rebuild_tree("Item Group", "parent_item_group")
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
def move_it_back(self):
|
def move_it_back(self):
|
||||||
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
||||||
group_b.parent_item_group = "All Item Groups"
|
group_b.parent_item_group = "All Item Groups"
|
||||||
group_b.save()
|
group_b.save()
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
def test_move_group_into_another(self):
|
def test_move_group_into_another(self):
|
||||||
# before move
|
# before move
|
||||||
old_lft, old_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
old_lft, old_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
||||||
|
|
||||||
# put B under C
|
# put B under C
|
||||||
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
||||||
lft, rgt = group_b.lft, group_b.rgt
|
lft, rgt = group_b.lft, group_b.rgt
|
||||||
|
|
||||||
group_b.parent_item_group = "_Test Item Group C"
|
group_b.parent_item_group = "_Test Item Group C"
|
||||||
group_b.save()
|
group_b.save()
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
# after move
|
# after move
|
||||||
new_lft, new_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
new_lft, new_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
||||||
|
|
||||||
# lft should reduce
|
# lft should reduce
|
||||||
self.assertEquals(old_lft - new_lft, rgt - lft + 1)
|
self.assertEquals(old_lft - new_lft, rgt - lft + 1)
|
||||||
|
|
||||||
# adjacent siblings, hence rgt diff will be 0
|
# adjacent siblings, hence rgt diff will be 0
|
||||||
self.assertEquals(new_rgt - old_rgt, 0)
|
self.assertEquals(new_rgt - old_rgt, 0)
|
||||||
|
|
||||||
self.move_it_back()
|
self.move_it_back()
|
||||||
|
|
||||||
def test_move_group_into_root(self):
|
def test_move_group_into_root(self):
|
||||||
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
|
||||||
group_b.parent_item_group = ""
|
group_b.parent_item_group = ""
|
||||||
@ -108,101 +107,101 @@ class TestItem(unittest.TestCase):
|
|||||||
|
|
||||||
# trick! works because it hasn't been rolled back :D
|
# trick! works because it hasn't been rolled back :D
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
self.move_it_back()
|
self.move_it_back()
|
||||||
|
|
||||||
def print_tree(self):
|
def print_tree(self):
|
||||||
import json
|
import json
|
||||||
print json.dumps(frappe.db.sql("select name, lft, rgt from `tabItem Group` order by lft"), indent=1)
|
print json.dumps(frappe.db.sql("select name, lft, rgt from `tabItem Group` order by lft"), indent=1)
|
||||||
|
|
||||||
def test_move_leaf_into_another_group(self):
|
def test_move_leaf_into_another_group(self):
|
||||||
# before move
|
# before move
|
||||||
old_lft, old_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
old_lft, old_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
||||||
|
|
||||||
group_b_3 = frappe.get_doc("Item Group", "_Test Item Group B - 3")
|
group_b_3 = frappe.get_doc("Item Group", "_Test Item Group B - 3")
|
||||||
lft, rgt = group_b_3.lft, group_b_3.rgt
|
lft, rgt = group_b_3.lft, group_b_3.rgt
|
||||||
|
|
||||||
# child of right sibling is moved into it
|
# child of right sibling is moved into it
|
||||||
group_b_3.parent_item_group = "_Test Item Group C"
|
group_b_3.parent_item_group = "_Test Item Group C"
|
||||||
group_b_3.save()
|
group_b_3.save()
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
new_lft, new_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
new_lft, new_rgt = frappe.db.get_value("Item Group", "_Test Item Group C", ["lft", "rgt"])
|
||||||
|
|
||||||
# lft should remain the same
|
# lft should remain the same
|
||||||
self.assertEquals(old_lft - new_lft, 0)
|
self.assertEquals(old_lft - new_lft, 0)
|
||||||
|
|
||||||
# rgt should increase
|
# rgt should increase
|
||||||
self.assertEquals(new_rgt - old_rgt, rgt - lft + 1)
|
self.assertEquals(new_rgt - old_rgt, rgt - lft + 1)
|
||||||
|
|
||||||
# move it back
|
# move it back
|
||||||
group_b_3 = frappe.get_doc("Item Group", "_Test Item Group B - 3")
|
group_b_3 = frappe.get_doc("Item Group", "_Test Item Group B - 3")
|
||||||
group_b_3.parent_item_group = "_Test Item Group B"
|
group_b_3.parent_item_group = "_Test Item Group B"
|
||||||
group_b_3.save()
|
group_b_3.save()
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
def test_delete_leaf(self):
|
def test_delete_leaf(self):
|
||||||
# for checking later
|
# for checking later
|
||||||
parent_item_group = frappe.db.get_value("Item Group", "_Test Item Group B - 3", "parent_item_group")
|
parent_item_group = frappe.db.get_value("Item Group", "_Test Item Group B - 3", "parent_item_group")
|
||||||
rgt = frappe.db.get_value("Item Group", parent_item_group, "rgt")
|
rgt = frappe.db.get_value("Item Group", parent_item_group, "rgt")
|
||||||
|
|
||||||
ancestors = get_ancestors_of("Item Group", "_Test Item Group B - 3")
|
ancestors = get_ancestors_of("Item Group", "_Test Item Group B - 3")
|
||||||
ancestors = frappe.db.sql("""select name, rgt from `tabItem Group`
|
ancestors = frappe.db.sql("""select name, rgt from `tabItem Group`
|
||||||
where name in ({})""".format(", ".join(["%s"]*len(ancestors))), tuple(ancestors), as_dict=True)
|
where name in ({})""".format(", ".join(["%s"]*len(ancestors))), tuple(ancestors), as_dict=True)
|
||||||
|
|
||||||
frappe.delete_doc("Item Group", "_Test Item Group B - 3")
|
frappe.delete_doc("Item Group", "_Test Item Group B - 3")
|
||||||
records_to_test = test_records[2:]
|
records_to_test = test_records[2:]
|
||||||
del records_to_test[4]
|
del records_to_test[4]
|
||||||
self.test_basic_tree(records=records_to_test)
|
self.test_basic_tree(records=records_to_test)
|
||||||
|
|
||||||
# rgt of each ancestor would reduce by 2
|
# rgt of each ancestor would reduce by 2
|
||||||
for item_group in ancestors:
|
for item_group in ancestors:
|
||||||
new_lft, new_rgt = frappe.db.get_value("Item Group", item_group.name, ["lft", "rgt"])
|
new_lft, new_rgt = frappe.db.get_value("Item Group", item_group.name, ["lft", "rgt"])
|
||||||
self.assertEquals(new_rgt, item_group.rgt - 2)
|
self.assertEquals(new_rgt, item_group.rgt - 2)
|
||||||
|
|
||||||
# insert it back
|
# insert it back
|
||||||
frappe.copy_doc(test_records[6]).insert()
|
frappe.copy_doc(test_records[6]).insert()
|
||||||
|
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
def test_delete_group(self):
|
def test_delete_group(self):
|
||||||
# cannot delete group with child, but can delete leaf
|
# cannot delete group with child, but can delete leaf
|
||||||
self.assertRaises(NestedSetChildExistsError, frappe.delete_doc, "Item Group", "_Test Item Group B")
|
self.assertRaises(NestedSetChildExistsError, frappe.delete_doc, "Item Group", "_Test Item Group B")
|
||||||
|
|
||||||
def test_merge_groups(self):
|
def test_merge_groups(self):
|
||||||
frappe.rename_doc("Item Group", "_Test Item Group B", "_Test Item Group C", merge=True)
|
frappe.rename_doc("Item Group", "_Test Item Group B", "_Test Item Group C", merge=True)
|
||||||
records_to_test = test_records[2:]
|
records_to_test = test_records[2:]
|
||||||
del records_to_test[1]
|
del records_to_test[1]
|
||||||
self.test_basic_tree(records=records_to_test)
|
self.test_basic_tree(records=records_to_test)
|
||||||
|
|
||||||
# insert Group B back
|
# insert Group B back
|
||||||
frappe.copy_doc(test_records[3]).insert()
|
frappe.copy_doc(test_records[3]).insert()
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
# move its children back
|
# move its children back
|
||||||
for name in frappe.db.sql_list("""select name from `tabItem Group`
|
for name in frappe.db.sql_list("""select name from `tabItem Group`
|
||||||
where parent_item_group='_Test Item Group C'"""):
|
where parent_item_group='_Test Item Group C'"""):
|
||||||
|
|
||||||
doc = frappe.get_doc("Item Group", name)
|
doc = frappe.get_doc("Item Group", name)
|
||||||
doc.parent_item_group = "_Test Item Group B"
|
doc.parent_item_group = "_Test Item Group B"
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
def test_merge_leaves(self):
|
def test_merge_leaves(self):
|
||||||
frappe.rename_doc("Item Group", "_Test Item Group B - 2", "_Test Item Group B - 1", merge=True)
|
frappe.rename_doc("Item Group", "_Test Item Group B - 2", "_Test Item Group B - 1", merge=True)
|
||||||
records_to_test = test_records[2:]
|
records_to_test = test_records[2:]
|
||||||
del records_to_test[3]
|
del records_to_test[3]
|
||||||
self.test_basic_tree(records=records_to_test)
|
self.test_basic_tree(records=records_to_test)
|
||||||
|
|
||||||
# insert Group B - 2back
|
# insert Group B - 2back
|
||||||
frappe.copy_doc(test_records[5]).insert()
|
frappe.copy_doc(test_records[5]).insert()
|
||||||
self.test_basic_tree()
|
self.test_basic_tree()
|
||||||
|
|
||||||
def test_merge_leaf_into_group(self):
|
def test_merge_leaf_into_group(self):
|
||||||
self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B - 3",
|
self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B - 3",
|
||||||
"_Test Item Group B", merge=True)
|
"_Test Item Group B", merge=True)
|
||||||
|
|
||||||
def test_merge_group_into_leaf(self):
|
def test_merge_group_into_leaf(self):
|
||||||
self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B",
|
self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B",
|
||||||
"_Test Item Group B - 3", merge=True)
|
"_Test Item Group B - 3", merge=True)
|
||||||
|
@ -4,9 +4,12 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cint, cstr, filter_strip_join
|
from frappe.utils import cint, cstr, filter_strip_join
|
||||||
from frappe.model.document import Document
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
|
|
||||||
|
class SalesPartner(WebsiteGenerator):
|
||||||
|
def autoname(self):
|
||||||
|
self.name = self.partner_name
|
||||||
|
|
||||||
class SalesPartner(Document):
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.partner_website and not self.partner_website.startswith("http"):
|
if self.partner_website and not self.partner_website.startswith("http"):
|
||||||
self.partner_website = "http://" + self.partner_website
|
self.partner_website = "http://" + self.partner_website
|
||||||
@ -14,8 +17,8 @@ class SalesPartner(Document):
|
|||||||
def get_contacts(self, nm):
|
def get_contacts(self, nm):
|
||||||
if nm:
|
if nm:
|
||||||
return frappe.db.convert_to_lists(frappe.db.sql("""
|
return frappe.db.convert_to_lists(frappe.db.sql("""
|
||||||
select name, CONCAT(IFNULL(first_name,''),
|
select name, CONCAT(IFNULL(first_name,''),
|
||||||
' ',IFNULL(last_name,'')),contact_no,email_id
|
' ',IFNULL(last_name,'')),contact_no,email_id
|
||||||
from `tabContact` where sales_partner = %s""", nm))
|
from `tabContact` where sales_partner = %s""", nm))
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
@ -15,19 +15,22 @@ from erpnext.controllers.selling_controller import SellingController
|
|||||||
class DeliveryNote(SellingController):
|
class DeliveryNote(SellingController):
|
||||||
tname = 'Delivery Note Item'
|
tname = 'Delivery Note Item'
|
||||||
fname = 'delivery_note_details'
|
fname = 'delivery_note_details'
|
||||||
status_updater = [{
|
|
||||||
'source_dt': 'Delivery Note Item',
|
def __init__(self, arg1, arg2=None):
|
||||||
'target_dt': 'Sales Order Item',
|
super(DeliveryNote, self).__init__(arg1, arg2)
|
||||||
'join_field': 'prevdoc_detail_docname',
|
self.status_updater = [{
|
||||||
'target_field': 'delivered_qty',
|
'source_dt': 'Delivery Note Item',
|
||||||
'target_parent_dt': 'Sales Order',
|
'target_dt': 'Sales Order Item',
|
||||||
'target_parent_field': 'per_delivered',
|
'join_field': 'prevdoc_detail_docname',
|
||||||
'target_ref_field': 'qty',
|
'target_field': 'delivered_qty',
|
||||||
'source_field': 'qty',
|
'target_parent_dt': 'Sales Order',
|
||||||
'percent_join_field': 'against_sales_order',
|
'target_parent_field': 'per_delivered',
|
||||||
'status_field': 'delivery_status',
|
'target_ref_field': 'qty',
|
||||||
'keyword': 'Delivered'
|
'source_field': 'qty',
|
||||||
}]
|
'percent_join_field': 'against_sales_order',
|
||||||
|
'status_field': 'delivery_status',
|
||||||
|
'keyword': 'Delivered'
|
||||||
|
}]
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabSales Invoice Item`
|
billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabSales Invoice Item`
|
||||||
|
@ -11,76 +11,76 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_
|
|||||||
|
|
||||||
def _insert_purchase_receipt(item_code=None):
|
def _insert_purchase_receipt(item_code=None):
|
||||||
if not item_code:
|
if not item_code:
|
||||||
item_code = pr_test_records[0][1]["item_code"]
|
item_code = pr_test_records[0]["purchase_receipt_details"][0]["item_code"]
|
||||||
|
|
||||||
pr = frappe.copy_doc(pr_test_records[0])
|
pr = frappe.copy_doc(pr_test_records[0])
|
||||||
pr.get("purchase_receipt_details")[0].item_code = item_code
|
pr.get("purchase_receipt_details")[0].item_code = item_code
|
||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
class TestDeliveryNote(unittest.TestCase):
|
class TestDeliveryNote(unittest.TestCase):
|
||||||
def test_over_billing_against_dn(self):
|
def test_over_billing_against_dn(self):
|
||||||
self.clear_stock_account_balance()
|
self.clear_stock_account_balance()
|
||||||
_insert_purchase_receipt()
|
_insert_purchase_receipt()
|
||||||
|
|
||||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
||||||
_insert_purchase_receipt()
|
_insert_purchase_receipt()
|
||||||
dn = frappe.copy_doc(test_records[0]).insert()
|
dn = frappe.copy_doc(test_records[0]).insert()
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, make_sales_invoice,
|
self.assertRaises(frappe.ValidationError, make_sales_invoice,
|
||||||
dn.name)
|
dn.name)
|
||||||
|
|
||||||
dn = frappe.get_doc("Delivery Note", dn.name)
|
dn = frappe.get_doc("Delivery Note", dn.name)
|
||||||
dn.submit()
|
dn.submit()
|
||||||
si = make_sales_invoice(dn.name)
|
si = make_sales_invoice(dn.name)
|
||||||
|
|
||||||
self.assertEquals(len(si), len(dn))
|
self.assertEquals(len(si.get("entries")), len(dn.get("delivery_note_details")))
|
||||||
|
|
||||||
# modify amount
|
# modify amount
|
||||||
si[1]["rate"] = 200
|
si.get("entries")[0].rate = 200
|
||||||
self.assertRaises(frappe.ValidationError, frappe.get_doc(si).insert)
|
self.assertRaises(frappe.ValidationError, frappe.get_doc(si).insert)
|
||||||
|
|
||||||
|
|
||||||
def test_delivery_note_no_gl_entry(self):
|
def test_delivery_note_no_gl_entry(self):
|
||||||
self.clear_stock_account_balance()
|
self.clear_stock_account_balance()
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 0)
|
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 0)
|
||||||
|
|
||||||
_insert_purchase_receipt()
|
_insert_purchase_receipt()
|
||||||
|
|
||||||
dn = frappe.copy_doc(test_records[0])
|
dn = frappe.copy_doc(test_records[0])
|
||||||
dn.insert()
|
dn.insert()
|
||||||
dn.submit()
|
dn.submit()
|
||||||
|
|
||||||
stock_value, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
|
stock_value, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
|
||||||
{"voucher_type": "Delivery Note", "voucher_no": dn.name,
|
{"voucher_type": "Delivery Note", "voucher_no": dn.name,
|
||||||
"item_code": "_Test Item"}, ["stock_value", "stock_value_difference"])
|
"item_code": "_Test Item"}, ["stock_value", "stock_value_difference"])
|
||||||
self.assertEqual(stock_value, 0)
|
self.assertEqual(stock_value, 0)
|
||||||
self.assertEqual(stock_value_difference, -375)
|
self.assertEqual(stock_value_difference, -375)
|
||||||
|
|
||||||
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
|
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
|
||||||
|
|
||||||
def test_delivery_note_gl_entry(self):
|
def test_delivery_note_gl_entry(self):
|
||||||
self.clear_stock_account_balance()
|
self.clear_stock_account_balance()
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
||||||
frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO")
|
frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO")
|
||||||
|
|
||||||
_insert_purchase_receipt()
|
_insert_purchase_receipt()
|
||||||
|
|
||||||
dn = frappe.copy_doc(test_records[0])
|
dn = frappe.copy_doc(test_records[0])
|
||||||
dn.get("delivery_note_details")[0].expense_account = "Cost of Goods Sold - _TC"
|
dn.get("delivery_note_details")[0].expense_account = "Cost of Goods Sold - _TC"
|
||||||
dn.get("delivery_note_details")[0].cost_center = "Main - _TC"
|
dn.get("delivery_note_details")[0].cost_center = "Main - _TC"
|
||||||
|
|
||||||
stock_in_hand_account = frappe.db.get_value("Account",
|
stock_in_hand_account = frappe.db.get_value("Account",
|
||||||
{"master_name": dn.get("delivery_note_details")[0].warehouse})
|
{"master_name": dn.get("delivery_note_details")[0].warehouse})
|
||||||
|
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
prev_bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
prev_bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
||||||
|
|
||||||
dn.insert()
|
dn.insert()
|
||||||
dn.submit()
|
dn.submit()
|
||||||
|
|
||||||
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
expected_values = {
|
expected_values = {
|
||||||
@ -89,20 +89,20 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
||||||
|
|
||||||
# check stock in hand balance
|
# check stock in hand balance
|
||||||
bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
||||||
self.assertEquals(bal, prev_bal - 375.0)
|
self.assertEquals(bal, prev_bal - 375.0)
|
||||||
|
|
||||||
# back dated purchase receipt
|
# back dated purchase receipt
|
||||||
pr = frappe.copy_doc(pr_test_records[0])
|
pr = frappe.copy_doc(pr_test_records[0])
|
||||||
pr.posting_date = "2013-01-01"
|
pr.posting_date = "2013-01-01"
|
||||||
pr.get("purchase_receipt_details")[0].rate = 100
|
pr.get("purchase_receipt_details")[0].rate = 100
|
||||||
pr.get("purchase_receipt_details")[0].base_amount = 100
|
pr.get("purchase_receipt_details")[0].base_amount = 100
|
||||||
|
|
||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
expected_values = {
|
expected_values = {
|
||||||
@ -111,71 +111,71 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
||||||
|
|
||||||
dn.cancel()
|
dn.cancel()
|
||||||
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
|
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_delivery_note_gl_entry_packing_item(self):
|
def test_delivery_note_gl_entry_packing_item(self):
|
||||||
self.clear_stock_account_balance()
|
self.clear_stock_account_balance()
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
|
|
||||||
_insert_purchase_receipt()
|
_insert_purchase_receipt()
|
||||||
_insert_purchase_receipt("_Test Item Home Desktop 100")
|
_insert_purchase_receipt("_Test Item Home Desktop 100")
|
||||||
|
|
||||||
dn = frappe.copy_doc(test_records[0])
|
dn = frappe.copy_doc(test_records[0])
|
||||||
dn.get("delivery_note_details")[0].item_code = "_Test Sales BOM Item"
|
dn.get("delivery_note_details")[0].item_code = "_Test Sales BOM Item"
|
||||||
dn.get("delivery_note_details")[0].qty = 1
|
dn.get("delivery_note_details")[0].qty = 1
|
||||||
|
|
||||||
stock_in_hand_account = frappe.db.get_value("Account",
|
stock_in_hand_account = frappe.db.get_value("Account",
|
||||||
{"master_name": dn.get("delivery_note_details")[0].warehouse})
|
{"master_name": dn.get("delivery_note_details")[0].warehouse})
|
||||||
|
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
prev_bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
prev_bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
||||||
|
|
||||||
dn.insert()
|
dn.insert()
|
||||||
dn.submit()
|
dn.submit()
|
||||||
|
|
||||||
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
gl_entries = get_gl_entries("Delivery Note", dn.name)
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = {
|
expected_values = {
|
||||||
stock_in_hand_account: [0.0, 525],
|
stock_in_hand_account: [0.0, 525],
|
||||||
"Cost of Goods Sold - _TC": [525.0, 0.0]
|
"Cost of Goods Sold - _TC": [525.0, 0.0]
|
||||||
}
|
}
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
|
||||||
|
|
||||||
# check stock in hand balance
|
# check stock in hand balance
|
||||||
bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
bal = get_balance_on(stock_in_hand_account, dn.posting_date)
|
||||||
self.assertEquals(bal, prev_bal - 525.0)
|
self.assertEquals(bal, prev_bal - 525.0)
|
||||||
|
|
||||||
dn.cancel()
|
dn.cancel()
|
||||||
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
|
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
|
||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_serialized(self):
|
def test_serialized(self):
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
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
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
se = make_serialized_item()
|
se = make_serialized_item()
|
||||||
serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)
|
serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)
|
||||||
|
|
||||||
dn = frappe.copy_doc(test_records[0])
|
dn = frappe.copy_doc(test_records[0])
|
||||||
dn.get("delivery_note_details")[0].item_code = "_Test Serialized Item With Series"
|
dn.get("delivery_note_details")[0].item_code = "_Test Serialized Item With Series"
|
||||||
dn.get("delivery_note_details")[0].qty = 1
|
dn.get("delivery_note_details")[0].qty = 1
|
||||||
dn.get("delivery_note_details")[0].serial_no = serial_nos[0]
|
dn.get("delivery_note_details")[0].serial_no = serial_nos[0]
|
||||||
dn.insert()
|
dn.insert()
|
||||||
dn.submit()
|
dn.submit()
|
||||||
|
|
||||||
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Delivered")
|
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Delivered")
|
||||||
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
|
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
|
||||||
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
|
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
|
||||||
"delivery_document_no"), dn.name)
|
"delivery_document_no"), dn.name)
|
||||||
|
|
||||||
return dn
|
return dn
|
||||||
|
|
||||||
def test_serialized_cancel(self):
|
def test_serialized_cancel(self):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
dn = self.test_serialized()
|
dn = self.test_serialized()
|
||||||
@ -185,20 +185,20 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Available")
|
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Available")
|
||||||
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
|
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
|
||||||
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
|
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
|
||||||
"delivery_document_no"))
|
"delivery_document_no"))
|
||||||
|
|
||||||
def test_serialize_status(self):
|
def test_serialize_status(self):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||||
|
|
||||||
se = make_serialized_item()
|
se = make_serialized_item()
|
||||||
serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)
|
serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)
|
||||||
|
|
||||||
sr = frappe.get_doc("Serial No", serial_nos[0])
|
sr = frappe.get_doc("Serial No", serial_nos[0])
|
||||||
sr.status = "Not Available"
|
sr.status = "Not Available"
|
||||||
sr.save()
|
sr.save()
|
||||||
|
|
||||||
dn = frappe.copy_doc(test_records[0])
|
dn = frappe.copy_doc(test_records[0])
|
||||||
dn.get("delivery_note_details")[0].item_code = "_Test Serialized Item With Series"
|
dn.get("delivery_note_details")[0].item_code = "_Test Serialized Item With Series"
|
||||||
dn.get("delivery_note_details")[0].qty = 1
|
dn.get("delivery_note_details")[0].qty = 1
|
||||||
@ -206,7 +206,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
dn.insert()
|
dn.insert()
|
||||||
|
|
||||||
self.assertRaises(SerialNoStatusError, dn.submit)
|
self.assertRaises(SerialNoStatusError, dn.submit)
|
||||||
|
|
||||||
def clear_stock_account_balance(self):
|
def clear_stock_account_balance(self):
|
||||||
frappe.db.sql("""delete from `tabBin`""")
|
frappe.db.sql("""delete from `tabBin`""")
|
||||||
frappe.db.sql("delete from `tabStock Ledger Entry`")
|
frappe.db.sql("delete from `tabStock Ledger Entry`")
|
||||||
@ -214,4 +214,4 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
test_dependencies = ["Sales BOM"]
|
test_dependencies = ["Sales BOM"]
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Delivery Note')
|
test_records = frappe.get_test_records('Delivery Note')
|
||||||
|
@ -3,32 +3,29 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import cstr, flt, getdate, now_datetime, formatdate
|
|
||||||
|
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
|
from frappe.utils import cstr, flt, getdate, now_datetime, formatdate
|
||||||
from frappe.model.controller import DocListController
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
|
|
||||||
class WarehouseNotSet(Exception): pass
|
class WarehouseNotSet(Exception): pass
|
||||||
|
|
||||||
class Item(DocListController):
|
class Item(WebsiteGenerator):
|
||||||
def onload(self):
|
def onload(self):
|
||||||
self.set("__sle_exists", self.check_if_sle_exists())
|
self.set("__sle_exists", self.check_if_sle_exists())
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
if frappe.db.get_default("item_naming_by")=="Naming Series":
|
if frappe.db.get_default("item_naming_by")=="Naming Series":
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
self.item_code = make_autoname(self.naming_series+'.#####')
|
self.item_code = make_autoname(self.naming_series+'.#####')
|
||||||
elif not self.item_code:
|
elif not self.item_code:
|
||||||
msgprint(_("Item Code (item_code) is mandatory because Item naming is not sequential."), raise_exception=1)
|
msgprint(_("Item Code (item_code) is mandatory because Item naming is not sequential."), raise_exception=1)
|
||||||
|
|
||||||
self.name = self.item_code
|
self.name = self.item_code
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.stock_uom:
|
if not self.stock_uom:
|
||||||
msgprint(_("Please enter Default Unit of Measure"), raise_exception=1)
|
msgprint(_("Please enter Default Unit of Measure"), raise_exception=1)
|
||||||
|
|
||||||
self.check_warehouse_is_set_for_stock_item()
|
self.check_warehouse_is_set_for_stock_item()
|
||||||
self.check_stock_uom_with_bin()
|
self.check_stock_uom_with_bin()
|
||||||
self.add_default_uom_in_conversion_factor_table()
|
self.add_default_uom_in_conversion_factor_table()
|
||||||
@ -40,14 +37,15 @@ class Item(DocListController):
|
|||||||
self.validate_barcode()
|
self.validate_barcode()
|
||||||
self.cant_change()
|
self.cant_change()
|
||||||
self.validate_item_type_for_reorder()
|
self.validate_item_type_for_reorder()
|
||||||
|
|
||||||
if not self.parent_website_route:
|
if not self.parent_website_route:
|
||||||
self.parent_website_route = frappe.get_website_route("Item Group", self.item_group)
|
self.parent_website_route = frappe.get_website_route("Item Group", self.item_group)
|
||||||
|
|
||||||
if self.name:
|
if self.name:
|
||||||
self.old_page_name = frappe.db.get_value('Item', self.name, 'page_name')
|
self.old_page_name = frappe.db.get_value('Item', self.name, 'page_name')
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
|
super(Item, self).on_update()
|
||||||
self.validate_name_with_item_group()
|
self.validate_name_with_item_group()
|
||||||
self.update_item_price()
|
self.update_item_price()
|
||||||
|
|
||||||
@ -55,46 +53,46 @@ class Item(DocListController):
|
|||||||
if self.is_stock_item=="Yes" and not self.default_warehouse:
|
if self.is_stock_item=="Yes" and not self.default_warehouse:
|
||||||
frappe.msgprint(_("Default Warehouse is mandatory for Stock Item."),
|
frappe.msgprint(_("Default Warehouse is mandatory for Stock Item."),
|
||||||
raise_exception=WarehouseNotSet)
|
raise_exception=WarehouseNotSet)
|
||||||
|
|
||||||
def add_default_uom_in_conversion_factor_table(self):
|
def add_default_uom_in_conversion_factor_table(self):
|
||||||
uom_conv_list = [d.uom for d in self.get("uom_conversion_details")]
|
uom_conv_list = [d.uom for d in self.get("uom_conversion_details")]
|
||||||
if self.stock_uom not in uom_conv_list:
|
if self.stock_uom not in uom_conv_list:
|
||||||
ch = self.append('uom_conversion_details', {})
|
ch = self.append('uom_conversion_details', {})
|
||||||
ch.uom = self.stock_uom
|
ch.uom = self.stock_uom
|
||||||
ch.conversion_factor = 1
|
ch.conversion_factor = 1
|
||||||
|
|
||||||
to_remove = []
|
to_remove = []
|
||||||
for d in self.get("uom_conversion_details"):
|
for d in self.get("uom_conversion_details"):
|
||||||
if d.conversion_factor == 1 and d.uom != self.stock_uom:
|
if d.conversion_factor == 1 and d.uom != self.stock_uom:
|
||||||
to_remove.append(d)
|
to_remove.append(d)
|
||||||
|
|
||||||
[self.remove(d) for d in to_remove]
|
[self.remove(d) for d in to_remove]
|
||||||
|
|
||||||
|
|
||||||
def check_stock_uom_with_bin(self):
|
def check_stock_uom_with_bin(self):
|
||||||
if not self.get("__islocal"):
|
if not self.get("__islocal"):
|
||||||
matched=True
|
matched=True
|
||||||
ref_uom = frappe.db.get_value("Stock Ledger Entry",
|
ref_uom = frappe.db.get_value("Stock Ledger Entry",
|
||||||
{"item_code": self.name}, "stock_uom")
|
{"item_code": self.name}, "stock_uom")
|
||||||
if ref_uom:
|
if ref_uom:
|
||||||
if cstr(ref_uom) != cstr(self.stock_uom):
|
if cstr(ref_uom) != cstr(self.stock_uom):
|
||||||
matched = False
|
matched = False
|
||||||
else:
|
else:
|
||||||
bin_list = frappe.db.sql("select * from tabBin where item_code=%s",
|
bin_list = frappe.db.sql("select * from tabBin where item_code=%s",
|
||||||
self.item_code, as_dict=1)
|
self.item_code, as_dict=1)
|
||||||
for bin in bin_list:
|
for bin in bin_list:
|
||||||
if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
|
if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
|
||||||
or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(self.stock_uom):
|
or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(self.stock_uom):
|
||||||
matched = False
|
matched = False
|
||||||
break
|
break
|
||||||
|
|
||||||
if matched and bin_list:
|
if matched and bin_list:
|
||||||
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""",
|
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""",
|
||||||
(self.stock_uom, self.name))
|
(self.stock_uom, self.name))
|
||||||
|
|
||||||
if not matched:
|
if not matched:
|
||||||
frappe.throw(_("Default Unit of Measure can not be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module."))
|
frappe.throw(_("Default Unit of Measure can not be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module."))
|
||||||
|
|
||||||
def validate_conversion_factor(self):
|
def validate_conversion_factor(self):
|
||||||
check_list = []
|
check_list = []
|
||||||
for d in self.get('uom_conversion_details'):
|
for d in self.get('uom_conversion_details'):
|
||||||
@ -105,12 +103,12 @@ class Item(DocListController):
|
|||||||
check_list.append(cstr(d.uom))
|
check_list.append(cstr(d.uom))
|
||||||
|
|
||||||
if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
|
if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
|
||||||
msgprint(_("""Conversion Factor of UOM: %s should be equal to 1. As UOM: %s is Stock UOM of Item: %s.""" %
|
msgprint(_("""Conversion Factor of UOM: %s should be equal to 1. As UOM: %s is Stock UOM of Item: %s.""" %
|
||||||
(d.uom, d.uom, self.name)), raise_exception=1)
|
(d.uom, d.uom, self.name)), raise_exception=1)
|
||||||
elif d.uom and cstr(d.uom)!= self.stock_uom and flt(d.conversion_factor) == 1:
|
elif d.uom and cstr(d.uom)!= self.stock_uom and flt(d.conversion_factor) == 1:
|
||||||
msgprint(_("""Conversion Factor of UOM: %s should not be equal to 1. As UOM: %s is not Stock UOM of Item: %s""" %
|
msgprint(_("""Conversion Factor of UOM: %s should not be equal to 1. As UOM: %s is not Stock UOM of Item: %s""" %
|
||||||
(d.uom, d.uom, self.name)), raise_exception=1)
|
(d.uom, d.uom, self.name)), raise_exception=1)
|
||||||
|
|
||||||
def validate_item_type(self):
|
def validate_item_type(self):
|
||||||
if cstr(self.is_manufactured_item) == "No":
|
if cstr(self.is_manufactured_item) == "No":
|
||||||
self.is_pro_applicable = "No"
|
self.is_pro_applicable = "No"
|
||||||
@ -121,25 +119,25 @@ class Item(DocListController):
|
|||||||
|
|
||||||
if self.has_serial_no == 'Yes' and self.is_stock_item == 'No':
|
if self.has_serial_no == 'Yes' and self.is_stock_item == 'No':
|
||||||
msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1)
|
msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1)
|
||||||
|
|
||||||
def check_for_active_boms(self):
|
def check_for_active_boms(self):
|
||||||
if self.is_purchase_item != "Yes":
|
if self.is_purchase_item != "Yes":
|
||||||
bom_mat = frappe.db.sql("""select distinct t1.parent
|
bom_mat = frappe.db.sql("""select distinct t1.parent
|
||||||
from `tabBOM Item` t1, `tabBOM` t2 where t2.name = t1.parent
|
from `tabBOM Item` t1, `tabBOM` t2 where t2.name = t1.parent
|
||||||
and t1.item_code =%s and ifnull(t1.bom_no, '') = '' and t2.is_active = 1
|
and t1.item_code =%s and ifnull(t1.bom_no, '') = '' and t2.is_active = 1
|
||||||
and t2.docstatus = 1 and t1.docstatus =1 """, self.name)
|
and t2.docstatus = 1 and t1.docstatus =1 """, self.name)
|
||||||
|
|
||||||
if bom_mat and bom_mat[0][0]:
|
if bom_mat and bom_mat[0][0]:
|
||||||
frappe.throw(_("Item must be a purchase item, \
|
frappe.throw(_("Item must be a purchase item, \
|
||||||
as it is present in one or many Active BOMs"))
|
as it is present in one or many Active BOMs"))
|
||||||
|
|
||||||
if self.is_manufactured_item != "Yes":
|
if self.is_manufactured_item != "Yes":
|
||||||
bom = frappe.db.sql("""select name from `tabBOM` where item = %s
|
bom = frappe.db.sql("""select name from `tabBOM` where item = %s
|
||||||
and is_active = 1""", (self.name,))
|
and is_active = 1""", (self.name,))
|
||||||
if bom and bom[0][0]:
|
if bom and bom[0][0]:
|
||||||
frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many \
|
frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many \
|
||||||
active BOMs present for this item"""))
|
active BOMs present for this item"""))
|
||||||
|
|
||||||
def fill_customer_code(self):
|
def fill_customer_code(self):
|
||||||
""" Append all the customer codes and insert into "customer_code" field of item table """
|
""" Append all the customer codes and insert into "customer_code" field of item table """
|
||||||
cust_code=[]
|
cust_code=[]
|
||||||
@ -153,7 +151,7 @@ class Item(DocListController):
|
|||||||
for d in self.get('item_tax'):
|
for d in self.get('item_tax'):
|
||||||
if d.tax_type:
|
if d.tax_type:
|
||||||
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
|
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
|
||||||
|
|
||||||
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
|
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
|
||||||
msgprint("'%s' is not Tax / Chargeable / Income / Expense Account" % d.tax_type, raise_exception=1)
|
msgprint("'%s' is not Tax / Chargeable / Income / Expense Account" % d.tax_type, raise_exception=1)
|
||||||
else:
|
else:
|
||||||
@ -161,34 +159,34 @@ class Item(DocListController):
|
|||||||
msgprint("Rate is entered twice for: '%s'" % d.tax_type, raise_exception=1)
|
msgprint("Rate is entered twice for: '%s'" % d.tax_type, raise_exception=1)
|
||||||
else:
|
else:
|
||||||
check_list.append(d.tax_type)
|
check_list.append(d.tax_type)
|
||||||
|
|
||||||
def validate_barcode(self):
|
def validate_barcode(self):
|
||||||
if self.barcode:
|
if self.barcode:
|
||||||
duplicate = frappe.db.sql("""select name from tabItem where barcode = %s
|
duplicate = frappe.db.sql("""select name from tabItem where barcode = %s
|
||||||
and name != %s""", (self.barcode, self.name))
|
and name != %s""", (self.barcode, self.name))
|
||||||
if duplicate:
|
if duplicate:
|
||||||
msgprint("Barcode: %s already used in item: %s" %
|
msgprint("Barcode: %s already used in item: %s" %
|
||||||
(self.barcode, cstr(duplicate[0][0])), raise_exception = 1)
|
(self.barcode, cstr(duplicate[0][0])), raise_exception = 1)
|
||||||
|
|
||||||
def cant_change(self):
|
def cant_change(self):
|
||||||
if not self.get("__islocal"):
|
if not self.get("__islocal"):
|
||||||
vals = frappe.db.get_value("Item", self.name,
|
vals = frappe.db.get_value("Item", self.name,
|
||||||
["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True)
|
["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True)
|
||||||
|
|
||||||
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
|
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
|
||||||
vals.has_serial_no != self.has_serial_no or
|
vals.has_serial_no != self.has_serial_no or
|
||||||
cstr(vals.valuation_method) != cstr(self.valuation_method)):
|
cstr(vals.valuation_method) != cstr(self.valuation_method)):
|
||||||
if self.check_if_sle_exists() == "exists":
|
if self.check_if_sle_exists() == "exists":
|
||||||
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Is Stock Item' and 'Valuation Method'"))
|
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Is Stock Item' and 'Valuation Method'"))
|
||||||
|
|
||||||
def validate_item_type_for_reorder(self):
|
def validate_item_type_for_reorder(self):
|
||||||
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
|
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
|
||||||
if not self.is_purchase_item:
|
if not self.is_purchase_item:
|
||||||
frappe.msgprint(_("""To set reorder level, item must be Purchase Item"""),
|
frappe.msgprint(_("""To set reorder level, item must be Purchase Item"""),
|
||||||
raise_exception=1)
|
raise_exception=1)
|
||||||
|
|
||||||
def check_if_sle_exists(self):
|
def check_if_sle_exists(self):
|
||||||
sle = frappe.db.sql("""select name from `tabStock Ledger Entry`
|
sle = frappe.db.sql("""select name from `tabStock Ledger Entry`
|
||||||
where item_code = %s""", self.name)
|
where item_code = %s""", self.name)
|
||||||
return sle and 'exists' or 'not exists'
|
return sle and 'exists' or 'not exists'
|
||||||
|
|
||||||
@ -196,11 +194,11 @@ class Item(DocListController):
|
|||||||
# causes problem with tree build
|
# causes problem with tree build
|
||||||
if frappe.db.exists("Item Group", self.name):
|
if frappe.db.exists("Item Group", self.name):
|
||||||
frappe.msgprint("An item group exists with same name (%s), \
|
frappe.msgprint("An item group exists with same name (%s), \
|
||||||
please change the item name or rename the item group" %
|
please change the item name or rename the item group" %
|
||||||
self.name, raise_exception=1)
|
self.name, raise_exception=1)
|
||||||
|
|
||||||
def update_item_price(self):
|
def update_item_price(self):
|
||||||
frappe.db.sql("""update `tabItem Price` set item_name=%s,
|
frappe.db.sql("""update `tabItem Price` set item_name=%s,
|
||||||
item_description=%s, modified=NOW() where item_code=%s""",
|
item_description=%s, modified=NOW() where item_code=%s""",
|
||||||
(self.item_name, self.description, self.name))
|
(self.item_name, self.description, self.name))
|
||||||
|
|
||||||
@ -209,9 +207,9 @@ class Item(DocListController):
|
|||||||
page_name_from = self.name
|
page_name_from = self.name
|
||||||
else:
|
else:
|
||||||
page_name_from = self.name + " " + self.item_name
|
page_name_from = self.name + " " + self.item_name
|
||||||
|
|
||||||
return page_name_from
|
return page_name_from
|
||||||
|
|
||||||
def get_tax_rate(self, tax_type):
|
def get_tax_rate(self, tax_type):
|
||||||
return { "tax_rate": frappe.db.get_value("Account", tax_type, "tax_rate") }
|
return { "tax_rate": frappe.db.get_value("Account", tax_type, "tax_rate") }
|
||||||
|
|
||||||
@ -223,8 +221,9 @@ class Item(DocListController):
|
|||||||
'description' : file and file[0]['description'] or ''
|
'description' : file and file[0]['description'] or ''
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
|
super(Item, self).on_trash()
|
||||||
frappe.db.sql("""delete from tabBin where item_code=%s""", self.item_code)
|
frappe.db.sql("""delete from tabBin where item_code=%s""", self.item_code)
|
||||||
|
|
||||||
def before_rename(self, olddn, newdn, merge=False):
|
def before_rename(self, olddn, newdn, merge=False):
|
||||||
@ -232,7 +231,7 @@ class Item(DocListController):
|
|||||||
# Validate properties before merging
|
# Validate properties before merging
|
||||||
if not frappe.db.exists("Item", newdn):
|
if not frappe.db.exists("Item", newdn):
|
||||||
frappe.throw(_("Item ") + newdn +_(" does not exists"))
|
frappe.throw(_("Item ") + newdn +_(" does not exists"))
|
||||||
|
|
||||||
field_list = ["stock_uom", "is_stock_item", "has_serial_no", "has_batch_no"]
|
field_list = ["stock_uom", "is_stock_item", "has_serial_no", "has_batch_no"]
|
||||||
new_properties = [cstr(d) for d in frappe.db.get_value("Item", newdn, field_list)]
|
new_properties = [cstr(d) for d in frappe.db.get_value("Item", newdn, field_list)]
|
||||||
if new_properties != [cstr(self.get(fld)) for fld in field_list]:
|
if new_properties != [cstr(self.get(fld)) for fld in field_list]:
|
||||||
@ -242,32 +241,33 @@ class Item(DocListController):
|
|||||||
frappe.db.sql("delete from `tabBin` where item_code=%s", olddn)
|
frappe.db.sql("delete from `tabBin` where item_code=%s", olddn)
|
||||||
|
|
||||||
def after_rename(self, olddn, newdn, merge):
|
def after_rename(self, olddn, newdn, merge):
|
||||||
|
super(Item, self).after_rename(olddn, newdn, merge)
|
||||||
|
|
||||||
frappe.db.set_value("Item", newdn, "item_code", newdn)
|
frappe.db.set_value("Item", newdn, "item_code", newdn)
|
||||||
|
|
||||||
if merge:
|
if merge:
|
||||||
self.set_last_purchase_rate(newdn)
|
self.set_last_purchase_rate(newdn)
|
||||||
self.recalculate_bin_qty(newdn)
|
self.recalculate_bin_qty(newdn)
|
||||||
|
|
||||||
def set_last_purchase_rate(self, newdn):
|
def set_last_purchase_rate(self, newdn):
|
||||||
last_purchase_rate = get_last_purchase_details(newdn).get("base_rate", 0)
|
last_purchase_rate = get_last_purchase_details(newdn).get("base_rate", 0)
|
||||||
frappe.db.set_value("Item", newdn, "last_purchase_rate", last_purchase_rate)
|
frappe.db.set_value("Item", newdn, "last_purchase_rate", last_purchase_rate)
|
||||||
|
|
||||||
def recalculate_bin_qty(self, newdn):
|
def recalculate_bin_qty(self, newdn):
|
||||||
from erpnext.utilities.repost_stock import repost_stock
|
from erpnext.utilities.repost_stock import repost_stock
|
||||||
frappe.db.auto_commit_on_many_writes = 1
|
frappe.db.auto_commit_on_many_writes = 1
|
||||||
frappe.db.set_default("allow_negative_stock", 1)
|
frappe.db.set_default("allow_negative_stock", 1)
|
||||||
|
|
||||||
for warehouse in frappe.db.sql("select name from `tabWarehouse`"):
|
for warehouse in frappe.db.sql("select name from `tabWarehouse`"):
|
||||||
repost_stock(newdn, warehouse[0])
|
repost_stock(newdn, warehouse[0])
|
||||||
|
|
||||||
frappe.db.set_default("allow_negative_stock",
|
frappe.db.set_default("allow_negative_stock",
|
||||||
frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
|
frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
|
||||||
frappe.db.auto_commit_on_many_writes = 0
|
frappe.db.auto_commit_on_many_writes = 0
|
||||||
|
|
||||||
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
|
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
|
||||||
if not end_of_life:
|
if not end_of_life:
|
||||||
end_of_life = frappe.db.get_value("Item", item_code, "end_of_life")
|
end_of_life = frappe.db.get_value("Item", item_code, "end_of_life")
|
||||||
|
|
||||||
if end_of_life and getdate(end_of_life) <= now_datetime().date():
|
if end_of_life and getdate(end_of_life) <= now_datetime().date():
|
||||||
msg = (_("Item") + " %(item_code)s: " + _("reached its end of life on") + \
|
msg = (_("Item") + " %(item_code)s: " + _("reached its end of life on") + \
|
||||||
" %(date)s. " + _("Please check") + ": %(end_of_life_label)s " + \
|
" %(date)s. " + _("Please check") + ": %(end_of_life_label)s " + \
|
||||||
@ -276,29 +276,29 @@ def validate_end_of_life(item_code, end_of_life=None, verbose=1):
|
|||||||
"date": formatdate(end_of_life),
|
"date": formatdate(end_of_life),
|
||||||
"end_of_life_label": frappe.get_meta("Item").get_label("end_of_life")
|
"end_of_life_label": frappe.get_meta("Item").get_label("end_of_life")
|
||||||
}
|
}
|
||||||
|
|
||||||
_msgprint(msg, verbose)
|
_msgprint(msg, verbose)
|
||||||
|
|
||||||
def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
|
def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
|
||||||
if not is_stock_item:
|
if not is_stock_item:
|
||||||
is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
|
is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
|
||||||
|
|
||||||
if is_stock_item != "Yes":
|
if is_stock_item != "Yes":
|
||||||
msg = (_("Item") + " %(item_code)s: " + _("is not a Stock Item")) % {
|
msg = (_("Item") + " %(item_code)s: " + _("is not a Stock Item")) % {
|
||||||
"item_code": item_code,
|
"item_code": item_code,
|
||||||
}
|
}
|
||||||
|
|
||||||
_msgprint(msg, verbose)
|
_msgprint(msg, verbose)
|
||||||
|
|
||||||
def validate_cancelled_item(item_code, docstatus=None, verbose=1):
|
def validate_cancelled_item(item_code, docstatus=None, verbose=1):
|
||||||
if docstatus is None:
|
if docstatus is None:
|
||||||
docstatus = frappe.db.get_value("Item", item_code, "docstatus")
|
docstatus = frappe.db.get_value("Item", item_code, "docstatus")
|
||||||
|
|
||||||
if docstatus == 2:
|
if docstatus == 2:
|
||||||
msg = (_("Item") + " %(item_code)s: " + _("is a cancelled Item")) % {
|
msg = (_("Item") + " %(item_code)s: " + _("is a cancelled Item")) % {
|
||||||
"item_code": item_code,
|
"item_code": item_code,
|
||||||
}
|
}
|
||||||
|
|
||||||
_msgprint(msg, verbose)
|
_msgprint(msg, verbose)
|
||||||
|
|
||||||
def _msgprint(msg, verbose):
|
def _msgprint(msg, verbose):
|
||||||
@ -306,22 +306,22 @@ def _msgprint(msg, verbose):
|
|||||||
msgprint(msg, raise_exception=True)
|
msgprint(msg, raise_exception=True)
|
||||||
else:
|
else:
|
||||||
raise frappe.ValidationError, msg
|
raise frappe.ValidationError, msg
|
||||||
|
|
||||||
|
|
||||||
def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
||||||
"""returns last purchase details in stock uom"""
|
"""returns last purchase details in stock uom"""
|
||||||
# get last purchase order item details
|
# get last purchase order item details
|
||||||
last_purchase_order = frappe.db.sql("""\
|
last_purchase_order = frappe.db.sql("""\
|
||||||
select po.name, po.transaction_date, po.conversion_rate,
|
select po.name, po.transaction_date, po.conversion_rate,
|
||||||
po_item.conversion_factor, po_item.base_price_list_rate,
|
po_item.conversion_factor, po_item.base_price_list_rate,
|
||||||
po_item.discount_percentage, po_item.base_rate
|
po_item.discount_percentage, po_item.base_rate
|
||||||
from `tabPurchase Order` po, `tabPurchase Order Item` po_item
|
from `tabPurchase Order` po, `tabPurchase Order Item` po_item
|
||||||
where po.docstatus = 1 and po_item.item_code = %s and po.name != %s and
|
where po.docstatus = 1 and po_item.item_code = %s and po.name != %s and
|
||||||
po.name = po_item.parent
|
po.name = po_item.parent
|
||||||
order by po.transaction_date desc, po.name desc
|
order by po.transaction_date desc, po.name desc
|
||||||
limit 1""", (item_code, cstr(doc_name)), as_dict=1)
|
limit 1""", (item_code, cstr(doc_name)), as_dict=1)
|
||||||
|
|
||||||
# get last purchase receipt item details
|
# get last purchase receipt item details
|
||||||
last_purchase_receipt = frappe.db.sql("""\
|
last_purchase_receipt = frappe.db.sql("""\
|
||||||
select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate,
|
select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate,
|
||||||
pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage,
|
pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage,
|
||||||
@ -342,16 +342,16 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
|||||||
# use purchase order
|
# use purchase order
|
||||||
last_purchase = last_purchase_order[0]
|
last_purchase = last_purchase_order[0]
|
||||||
purchase_date = purchase_order_date
|
purchase_date = purchase_order_date
|
||||||
|
|
||||||
elif (purchase_receipt_date > purchase_order_date) or \
|
elif (purchase_receipt_date > purchase_order_date) or \
|
||||||
(last_purchase_receipt and not last_purchase_order):
|
(last_purchase_receipt and not last_purchase_order):
|
||||||
# use purchase receipt
|
# use purchase receipt
|
||||||
last_purchase = last_purchase_receipt[0]
|
last_purchase = last_purchase_receipt[0]
|
||||||
purchase_date = purchase_receipt_date
|
purchase_date = purchase_receipt_date
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return frappe._dict()
|
return frappe._dict()
|
||||||
|
|
||||||
conversion_factor = flt(last_purchase.conversion_factor)
|
conversion_factor = flt(last_purchase.conversion_factor)
|
||||||
out = frappe._dict({
|
out = frappe._dict({
|
||||||
"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
|
"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
|
||||||
@ -366,5 +366,5 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
|||||||
"rate": out.base_rate / conversion_rate,
|
"rate": out.base_rate / conversion_rate,
|
||||||
"base_rate": out.base_rate
|
"base_rate": out.base_rate
|
||||||
})
|
})
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
@ -169,11 +169,10 @@ def update_completed_qty(doc, method):
|
|||||||
|
|
||||||
for mr_name, mr_items in material_request_map.items():
|
for mr_name, mr_items in material_request_map.items():
|
||||||
mr_obj = frappe.get_doc("Material Request", mr_name)
|
mr_obj = frappe.get_doc("Material Request", mr_name)
|
||||||
mr_doctype = frappe.get_meta("Material Request")
|
|
||||||
|
|
||||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||||
frappe.throw(_("Material Request") + ": %s, " % mr_obj.name
|
frappe.throw(_("Material Request") + ": %s, " % mr_obj.name
|
||||||
+ _(mr_doctype.get_label("status")) + " = %s. " % _(mr_obj.status)
|
+ _(mr_obj.meta.get_label("status")) + " = %s. " % _(mr_obj.status)
|
||||||
+ _("Cannot continue."), exc=frappe.InvalidStatusError)
|
+ _("Cannot continue."), exc=frappe.InvalidStatusError)
|
||||||
|
|
||||||
_update_requested_qty(doc, mr_obj, mr_items)
|
_update_requested_qty(doc, mr_obj, mr_items)
|
||||||
|
@ -24,7 +24,7 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.submit()
|
mr.submit()
|
||||||
po = make_purchase_order(mr.name)
|
po = make_purchase_order(mr.name)
|
||||||
|
|
||||||
self.assertEquals(po["doctype"], "Purchase Order")
|
self.assertEquals(po.doctype, "Purchase Order")
|
||||||
self.assertEquals(len(po.get("po_details")), len(mr.get("indent_details")))
|
self.assertEquals(len(po.get("po_details")), len(mr.get("indent_details")))
|
||||||
|
|
||||||
def test_make_supplier_quotation(self):
|
def test_make_supplier_quotation(self):
|
||||||
@ -38,7 +38,7 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.submit()
|
mr.submit()
|
||||||
sq = make_supplier_quotation(mr.name)
|
sq = make_supplier_quotation(mr.name)
|
||||||
|
|
||||||
self.assertEquals(sq["doctype"], "Supplier Quotation")
|
self.assertEquals(sq.doctype, "Supplier Quotation")
|
||||||
self.assertEquals(len(sq.get("quotation_items")), len(mr.get("indent_details")))
|
self.assertEquals(len(sq.get("quotation_items")), len(mr.get("indent_details")))
|
||||||
|
|
||||||
|
|
||||||
@ -55,14 +55,9 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.submit()
|
mr.submit()
|
||||||
se = make_stock_entry(mr.name)
|
se = make_stock_entry(mr.name)
|
||||||
|
|
||||||
self.assertEquals(se["doctype"], "Stock Entry")
|
self.assertEquals(se.doctype, "Stock Entry")
|
||||||
self.assertEquals(len(se.get("mtn_details")), len(mr.get("indent_details")))
|
self.assertEquals(len(se.get("mtn_details")), len(mr.get("indent_details")))
|
||||||
|
|
||||||
def _test_expected(self, doc, expected_values):
|
|
||||||
for i, expected in enumerate(expected_values):
|
|
||||||
for fieldname, val in expected.items():
|
|
||||||
self.assertEquals(val, doc.get(fieldname))
|
|
||||||
|
|
||||||
def _test_requested_qty(self, qty1, qty2):
|
def _test_requested_qty(self, qty1, qty2):
|
||||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
|
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty1)
|
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty1)
|
||||||
@ -116,28 +111,34 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.submit()
|
mr.submit()
|
||||||
|
|
||||||
# check if per complete is None
|
# check if per complete is None
|
||||||
self._test_expected(mr, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": None}])
|
self.assertEquals(mr.per_ordered, None)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
self._test_requested_qty(54.0, 3.0)
|
||||||
|
|
||||||
# map a purchase order
|
# map a purchase order
|
||||||
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||||
po_doc = make_purchase_order(mr.name)
|
po_doc = make_purchase_order(mr.name)
|
||||||
po_doc["supplier"] = "_Test Supplier"
|
po_doc.supplier = "_Test Supplier"
|
||||||
po_doc["transaction_date"] = "2013-07-07"
|
po_doc.transaction_date = "2013-07-07"
|
||||||
po_doc.get("po_details")[0]["qty"] = 27.0
|
po_doc.get("po_details")[0].qty = 27.0
|
||||||
po_doc.get("po_details")[1]["qty"] = 1.5
|
po_doc.get("po_details")[1].qty = 1.5
|
||||||
po_doc.get("po_details")[0]["schedule_date"] = "2013-07-09"
|
po_doc.get("po_details")[0].schedule_date = "2013-07-09"
|
||||||
po_doc.get("po_details")[1]["schedule_date"] = "2013-07-09"
|
po_doc.get("po_details")[1].schedule_date = "2013-07-09"
|
||||||
|
|
||||||
|
|
||||||
# check for stopped status of Material Request
|
# check for stopped status of Material Request
|
||||||
po = frappe.copy_doc(po_doc)
|
po = frappe.copy_doc(po_doc)
|
||||||
po.insert()
|
po.insert()
|
||||||
|
po.load_from_db()
|
||||||
mr.update_status('Stopped')
|
mr.update_status('Stopped')
|
||||||
self.assertRaises(frappe.ValidationError, po.submit)
|
self.assertRaises(frappe.InvalidStatusError, po.submit)
|
||||||
self.assertRaises(frappe.ValidationError, po.cancel)
|
frappe.db.set(po, "docstatus", 1)
|
||||||
|
self.assertRaises(frappe.InvalidStatusError, po.cancel)
|
||||||
|
|
||||||
|
# resubmit and check for per complete
|
||||||
|
mr.load_from_db()
|
||||||
mr.update_status('Submitted')
|
mr.update_status('Submitted')
|
||||||
po = frappe.copy_doc(po_doc)
|
po = frappe.copy_doc(po_doc)
|
||||||
po.insert()
|
po.insert()
|
||||||
@ -145,13 +146,18 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
|
|
||||||
# check if per complete is as expected
|
# check if per complete is as expected
|
||||||
mr.load_from_db()
|
mr.load_from_db()
|
||||||
self._test_expected(mr, [{"per_ordered": 50}, {"ordered_qty": 27.0}, {"ordered_qty": 1.5}])
|
self.assertEquals(mr.per_ordered, 50)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||||
self._test_requested_qty(27.0, 1.5)
|
self._test_requested_qty(27.0, 1.5)
|
||||||
|
|
||||||
po.cancel()
|
po.cancel()
|
||||||
# check if per complete is as expected
|
# check if per complete is as expected
|
||||||
mr.load_from_db()
|
mr.load_from_db()
|
||||||
self._test_expected(mr, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": None}])
|
self.assertEquals(mr.per_ordered, None)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, None)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, None)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
self._test_requested_qty(54.0, 3.0)
|
||||||
|
|
||||||
def test_completed_qty_for_transfer(self):
|
def test_completed_qty_for_transfer(self):
|
||||||
@ -165,7 +171,9 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.submit()
|
mr.submit()
|
||||||
|
|
||||||
# check if per complete is None
|
# check if per complete is None
|
||||||
self._test_expected(mr, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": None}])
|
self.assertEquals(mr.per_ordered, None)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
self._test_requested_qty(54.0, 3.0)
|
||||||
|
|
||||||
@ -198,8 +206,12 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
se = frappe.copy_doc(se_doc)
|
se = frappe.copy_doc(se_doc)
|
||||||
se.insert()
|
se.insert()
|
||||||
mr.update_status('Stopped')
|
mr.update_status('Stopped')
|
||||||
self.assertRaises(frappe.ValidationError, se.submit)
|
self.assertRaises(frappe.InvalidStatusError, se.submit)
|
||||||
self.assertRaises(frappe.ValidationError, se.cancel)
|
|
||||||
|
mr.update_status('Submitted')
|
||||||
|
se.submit()
|
||||||
|
mr.update_status('Stopped')
|
||||||
|
self.assertRaises(frappe.InvalidStatusError, se.cancel)
|
||||||
|
|
||||||
mr.update_status('Submitted')
|
mr.update_status('Submitted')
|
||||||
se = frappe.copy_doc(se_doc)
|
se = frappe.copy_doc(se_doc)
|
||||||
@ -208,13 +220,19 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
|
|
||||||
# check if per complete is as expected
|
# check if per complete is as expected
|
||||||
mr.load_from_db()
|
mr.load_from_db()
|
||||||
self._test_expected(mr, [{"per_ordered": 50}, {"ordered_qty": 27.0}, {"ordered_qty": 1.5}])
|
self.assertEquals(mr.per_ordered, 50)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||||
|
|
||||||
self._test_requested_qty(27.0, 1.5)
|
self._test_requested_qty(27.0, 1.5)
|
||||||
|
|
||||||
# check if per complete is as expected for Stock Entry cancelled
|
# check if per complete is as expected for Stock Entry cancelled
|
||||||
se.cancel()
|
se.cancel()
|
||||||
mr.load_from_db()
|
mr.load_from_db()
|
||||||
self._test_expected(mr, [{"per_ordered": 0}, {"ordered_qty": 0}, {"ordered_qty": 0}])
|
self.assertEquals(mr.per_ordered, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
self._test_requested_qty(54.0, 3.0)
|
||||||
|
|
||||||
def test_completed_qty_for_over_transfer(self):
|
def test_completed_qty_for_over_transfer(self):
|
||||||
@ -228,7 +246,9 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.submit()
|
mr.submit()
|
||||||
|
|
||||||
# check if per complete is None
|
# check if per complete is None
|
||||||
self._test_expected(mr, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": None}])
|
self.assertEquals(mr.per_ordered, None)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
self._test_requested_qty(54.0, 3.0)
|
||||||
|
|
||||||
@ -261,8 +281,8 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
se = frappe.copy_doc(se_doc)
|
se = frappe.copy_doc(se_doc)
|
||||||
se.insert()
|
se.insert()
|
||||||
mr.update_status('Stopped')
|
mr.update_status('Stopped')
|
||||||
self.assertRaises(frappe.ValidationError, se.submit)
|
self.assertRaises(frappe.InvalidStatusError, se.submit)
|
||||||
self.assertRaises(frappe.ValidationError, se.cancel)
|
self.assertRaises(frappe.InvalidStatusError, se.cancel)
|
||||||
|
|
||||||
mr.update_status('Submitted')
|
mr.update_status('Submitted')
|
||||||
se = frappe.copy_doc(se_doc)
|
se = frappe.copy_doc(se_doc)
|
||||||
@ -271,13 +291,19 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
|
|
||||||
# check if per complete is as expected
|
# check if per complete is as expected
|
||||||
mr.load_from_db()
|
mr.load_from_db()
|
||||||
self._test_expected(mr, [{"per_ordered": 100}, {"ordered_qty": 60.0}, {"ordered_qty": 3.0}])
|
|
||||||
|
self.assertEquals(mr.per_ordered, 100)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 60.0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 3.0)
|
||||||
self._test_requested_qty(0.0, 0.0)
|
self._test_requested_qty(0.0, 0.0)
|
||||||
|
|
||||||
# check if per complete is as expected for Stock Entry cancelled
|
# check if per complete is as expected for Stock Entry cancelled
|
||||||
se.cancel()
|
se.cancel()
|
||||||
mr.load_from_db()
|
mr.load_from_db()
|
||||||
self._test_expected(mr, [{"per_ordered": 0}, {"ordered_qty": 0}, {"ordered_qty": 0}])
|
self.assertEquals(mr.per_ordered, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
self._test_requested_qty(54.0, 3.0)
|
||||||
|
|
||||||
def test_incorrect_mapping_of_stock_entry(self):
|
def test_incorrect_mapping_of_stock_entry(self):
|
||||||
|
@ -14,19 +14,21 @@ from erpnext.controllers.buying_controller import BuyingController
|
|||||||
class PurchaseReceipt(BuyingController):
|
class PurchaseReceipt(BuyingController):
|
||||||
tname = 'Purchase Receipt Item'
|
tname = 'Purchase Receipt Item'
|
||||||
fname = 'purchase_receipt_details'
|
fname = 'purchase_receipt_details'
|
||||||
count = 0
|
|
||||||
status_updater = [{
|
def __init__(self, arg1, arg2=None):
|
||||||
'source_dt': 'Purchase Receipt Item',
|
super(PurchaseReceipt, self).__init__(arg1, arg2)
|
||||||
'target_dt': 'Purchase Order Item',
|
self.status_updater = [{
|
||||||
'join_field': 'prevdoc_detail_docname',
|
'source_dt': 'Purchase Receipt Item',
|
||||||
'target_field': 'received_qty',
|
'target_dt': 'Purchase Order Item',
|
||||||
'target_parent_dt': 'Purchase Order',
|
'join_field': 'prevdoc_detail_docname',
|
||||||
'target_parent_field': 'per_received',
|
'target_field': 'received_qty',
|
||||||
'target_ref_field': 'qty',
|
'target_parent_dt': 'Purchase Order',
|
||||||
'source_field': 'qty',
|
'target_parent_field': 'per_received',
|
||||||
'percent_join_field': 'prevdoc_docname',
|
'target_ref_field': 'qty',
|
||||||
}]
|
'source_field': 'qty',
|
||||||
|
'percent_join_field': 'prevdoc_docname',
|
||||||
|
}]
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabPurchase Invoice Item`
|
billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabPurchase Invoice Item`
|
||||||
where purchase_receipt=%s""", self.name)
|
where purchase_receipt=%s""", self.name)
|
||||||
@ -36,7 +38,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(PurchaseReceipt, self).validate()
|
super(PurchaseReceipt, self).validate()
|
||||||
|
|
||||||
self.po_required()
|
self.po_required()
|
||||||
|
|
||||||
if not self.status:
|
if not self.status:
|
||||||
@ -60,7 +62,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
# sub-contracting
|
# sub-contracting
|
||||||
self.validate_for_subcontracting()
|
self.validate_for_subcontracting()
|
||||||
self.update_raw_materials_supplied("pr_raw_material_details")
|
self.update_raw_materials_supplied("pr_raw_material_details")
|
||||||
|
|
||||||
self.update_valuation_rate("purchase_receipt_details")
|
self.update_valuation_rate("purchase_receipt_details")
|
||||||
|
|
||||||
def validate_rejected_warehouse(self):
|
def validate_rejected_warehouse(self):
|
||||||
@ -68,7 +70,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if flt(d.rejected_qty) and not d.rejected_warehouse:
|
if flt(d.rejected_qty) and not d.rejected_warehouse:
|
||||||
d.rejected_warehouse = self.rejected_warehouse
|
d.rejected_warehouse = self.rejected_warehouse
|
||||||
if not d.rejected_warehouse:
|
if not d.rejected_warehouse:
|
||||||
frappe.throw(_("Rejected Warehouse is mandatory against regected item"))
|
frappe.throw(_("Rejected Warehouse is mandatory against regected item"))
|
||||||
|
|
||||||
# validate accepted and rejected qty
|
# validate accepted and rejected qty
|
||||||
def validate_accepted_rejected_qty(self):
|
def validate_accepted_rejected_qty(self):
|
||||||
@ -99,7 +101,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if exists:
|
if exists:
|
||||||
frappe.msgprint("Another Purchase Receipt using the same Challan No. already exists.\
|
frappe.msgprint("Another Purchase Receipt using the same Challan No. already exists.\
|
||||||
Please enter a valid Challan No.", raise_exception=1)
|
Please enter a valid Challan No.", raise_exception=1)
|
||||||
|
|
||||||
def validate_with_previous_doc(self):
|
def validate_with_previous_doc(self):
|
||||||
super(PurchaseReceipt, self).validate_with_previous_doc(self.tname, {
|
super(PurchaseReceipt, self).validate_with_previous_doc(self.tname, {
|
||||||
"Purchase Order": {
|
"Purchase Order": {
|
||||||
@ -112,7 +114,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
"is_child_table": True
|
"is_child_table": True
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if cint(frappe.defaults.get_global_default('maintain_same_rate')):
|
if cint(frappe.defaults.get_global_default('maintain_same_rate')):
|
||||||
super(PurchaseReceipt, self).validate_with_previous_doc(self.tname, {
|
super(PurchaseReceipt, self).validate_with_previous_doc(self.tname, {
|
||||||
"Purchase Order Item": {
|
"Purchase Order Item": {
|
||||||
@ -121,7 +123,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
"is_child_table": True
|
"is_child_table": True
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def po_required(self):
|
def po_required(self):
|
||||||
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
|
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
|
||||||
@ -133,18 +135,18 @@ class PurchaseReceipt(BuyingController):
|
|||||||
def update_stock(self):
|
def update_stock(self):
|
||||||
sl_entries = []
|
sl_entries = []
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
|
|
||||||
for d in self.get('purchase_receipt_details'):
|
for d in self.get('purchase_receipt_details'):
|
||||||
if d.item_code in stock_items and d.warehouse:
|
if d.item_code in stock_items and d.warehouse:
|
||||||
pr_qty = flt(d.qty) * flt(d.conversion_factor)
|
pr_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
if pr_qty:
|
if pr_qty:
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
"actual_qty": flt(pr_qty),
|
"actual_qty": flt(pr_qty),
|
||||||
"serial_no": cstr(d.serial_no).strip(),
|
"serial_no": cstr(d.serial_no).strip(),
|
||||||
"incoming_rate": d.valuation_rate
|
"incoming_rate": d.valuation_rate
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if flt(d.rejected_qty) > 0:
|
if flt(d.rejected_qty) > 0:
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
"warehouse": d.rejected_warehouse,
|
"warehouse": d.rejected_warehouse,
|
||||||
@ -152,28 +154,28 @@ class PurchaseReceipt(BuyingController):
|
|||||||
"serial_no": cstr(d.rejected_serial_no).strip(),
|
"serial_no": cstr(d.rejected_serial_no).strip(),
|
||||||
"incoming_rate": d.valuation_rate
|
"incoming_rate": d.valuation_rate
|
||||||
}))
|
}))
|
||||||
|
|
||||||
self.bk_flush_supp_wh(sl_entries)
|
self.bk_flush_supp_wh(sl_entries)
|
||||||
self.make_sl_entries(sl_entries)
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
def update_ordered_qty(self):
|
def update_ordered_qty(self):
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
for d in self.get("purchase_receipt_details"):
|
for d in self.get("purchase_receipt_details"):
|
||||||
if d.item_code in stock_items and d.warehouse \
|
if d.item_code in stock_items and d.warehouse \
|
||||||
and cstr(d.prevdoc_doctype) == 'Purchase Order':
|
and cstr(d.prevdoc_doctype) == 'Purchase Order':
|
||||||
|
|
||||||
already_received_qty = self.get_already_received_qty(d.prevdoc_docname,
|
already_received_qty = self.get_already_received_qty(d.prevdoc_docname,
|
||||||
d.prevdoc_detail_docname)
|
d.prevdoc_detail_docname)
|
||||||
po_qty, ordered_warehouse = self.get_po_qty_and_warehouse(d.prevdoc_detail_docname)
|
po_qty, ordered_warehouse = self.get_po_qty_and_warehouse(d.prevdoc_detail_docname)
|
||||||
|
|
||||||
if not ordered_warehouse:
|
if not ordered_warehouse:
|
||||||
frappe.throw(_("Warehouse is missing in Purchase Order"))
|
frappe.throw(_("Warehouse is missing in Purchase Order"))
|
||||||
|
|
||||||
if already_received_qty + d.qty > po_qty:
|
if already_received_qty + d.qty > po_qty:
|
||||||
ordered_qty = - (po_qty - already_received_qty) * flt(d.conversion_factor)
|
ordered_qty = - (po_qty - already_received_qty) * flt(d.conversion_factor)
|
||||||
else:
|
else:
|
||||||
ordered_qty = - flt(d.qty) * flt(d.conversion_factor)
|
ordered_qty = - flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
update_bin({
|
update_bin({
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"warehouse": ordered_warehouse,
|
"warehouse": ordered_warehouse,
|
||||||
@ -182,20 +184,20 @@ class PurchaseReceipt(BuyingController):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def get_already_received_qty(self, po, po_detail):
|
def get_already_received_qty(self, po, po_detail):
|
||||||
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
||||||
where prevdoc_detail_docname = %s and docstatus = 1
|
where prevdoc_detail_docname = %s and docstatus = 1
|
||||||
and prevdoc_doctype='Purchase Order' and prevdoc_docname=%s
|
and prevdoc_doctype='Purchase Order' and prevdoc_docname=%s
|
||||||
and parent != %s""", (po_detail, po, self.name))
|
and parent != %s""", (po_detail, po, self.name))
|
||||||
return qty and flt(qty[0][0]) or 0.0
|
return qty and flt(qty[0][0]) or 0.0
|
||||||
|
|
||||||
def get_po_qty_and_warehouse(self, po_detail):
|
def get_po_qty_and_warehouse(self, po_detail):
|
||||||
po_qty, po_warehouse = frappe.db.get_value("Purchase Order Item", po_detail,
|
po_qty, po_warehouse = frappe.db.get_value("Purchase Order Item", po_detail,
|
||||||
["qty", "warehouse"])
|
["qty", "warehouse"])
|
||||||
return po_qty, po_warehouse
|
return po_qty, po_warehouse
|
||||||
|
|
||||||
def bk_flush_supp_wh(self, sl_entries):
|
def bk_flush_supp_wh(self, sl_entries):
|
||||||
for d in self.get('pr_raw_material_details'):
|
for d in self.get('pr_raw_material_details'):
|
||||||
# negative quantity is passed as raw material qty has to be decreased
|
# negative quantity is passed as raw material qty has to be decreased
|
||||||
# when PR is submitted and it has to be increased when PR is cancelled
|
# when PR is submitted and it has to be increased when PR is cancelled
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
"item_code": d.rm_item_code,
|
"item_code": d.rm_item_code,
|
||||||
@ -231,22 +233,22 @@ class PurchaseReceipt(BuyingController):
|
|||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
|
|
||||||
self.update_stock()
|
self.update_stock()
|
||||||
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||||
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
||||||
|
|
||||||
purchase_controller.update_last_purchase_rate(self, 1)
|
purchase_controller.update_last_purchase_rate(self, 1)
|
||||||
|
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
def check_next_docstatus(self):
|
def check_next_docstatus(self):
|
||||||
submit_rv = frappe.db.sql("""select t1.name
|
submit_rv = frappe.db.sql("""select t1.name
|
||||||
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
|
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
|
||||||
where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
|
where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
|
||||||
(self.name))
|
(self.name))
|
||||||
if submit_rv:
|
if submit_rv:
|
||||||
msgprint("Purchase Invoice : " + cstr(self.submit_rv[0][0]) + " has already been submitted !")
|
msgprint("Purchase Invoice : " + cstr(self.submit_rv[0][0]) + " has already been submitted !")
|
||||||
@ -258,25 +260,25 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
self.check_for_stopped_status(pc_obj)
|
self.check_for_stopped_status(pc_obj)
|
||||||
# Check if Purchase Invoice has been submitted against current Purchase Order
|
# Check if Purchase Invoice has been submitted against current Purchase Order
|
||||||
submitted = frappe.db.sql("""select t1.name
|
submitted = frappe.db.sql("""select t1.name
|
||||||
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
|
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
|
||||||
where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
|
where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
|
||||||
self.name)
|
self.name)
|
||||||
if submitted:
|
if submitted:
|
||||||
frappe.throw("Purchase Invoice : " + cstr(submitted[0][0]) +
|
frappe.throw("Purchase Invoice : " + cstr(submitted[0][0]) +
|
||||||
" has already been submitted !")
|
" has already been submitted !")
|
||||||
|
|
||||||
frappe.db.set(self,'status','Cancelled')
|
frappe.db.set(self,'status','Cancelled')
|
||||||
|
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
|
|
||||||
self.update_stock()
|
self.update_stock()
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
pc_obj.update_last_purchase_rate(self, 0)
|
pc_obj.update_last_purchase_rate(self, 0)
|
||||||
|
|
||||||
self.make_cancel_gl_entries()
|
self.make_cancel_gl_entries()
|
||||||
|
|
||||||
def get_current_stock(self):
|
def get_current_stock(self):
|
||||||
for d in self.get('pr_raw_material_details'):
|
for d in self.get('pr_raw_material_details'):
|
||||||
if self.supplier_warehouse:
|
if self.supplier_warehouse:
|
||||||
@ -285,42 +287,42 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
def get_rate(self,arg):
|
def get_rate(self,arg):
|
||||||
return frappe.get_doc('Purchase Common').get_rate(arg,self)
|
return frappe.get_doc('Purchase Common').get_rate(arg,self)
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None):
|
def get_gl_entries(self, warehouse_account=None):
|
||||||
against_stock_account = self.get_company_default("stock_received_but_not_billed")
|
against_stock_account = self.get_company_default("stock_received_but_not_billed")
|
||||||
|
|
||||||
gl_entries = super(PurchaseReceipt, self).get_gl_entries(warehouse_account, against_stock_account)
|
gl_entries = super(PurchaseReceipt, self).get_gl_entries(warehouse_account, against_stock_account)
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_invoice(source_name, target_doc=None):
|
def make_purchase_invoice(source_name, target_doc=None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
doc = frappe.get_doc(target)
|
doc = frappe.get_doc(target)
|
||||||
doc.run_method("set_missing_values")
|
doc.run_method("set_missing_values")
|
||||||
|
|
||||||
doclist = get_mapped_doc("Purchase Receipt", source_name, {
|
doclist = get_mapped_doc("Purchase Receipt", source_name, {
|
||||||
"Purchase Receipt": {
|
"Purchase Receipt": {
|
||||||
"doctype": "Purchase Invoice",
|
"doctype": "Purchase Invoice",
|
||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 1],
|
"docstatus": ["=", 1],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Purchase Receipt Item": {
|
"Purchase Receipt Item": {
|
||||||
"doctype": "Purchase Invoice Item",
|
"doctype": "Purchase Invoice Item",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
"name": "pr_detail",
|
"name": "pr_detail",
|
||||||
"parent": "purchase_receipt",
|
"parent": "purchase_receipt",
|
||||||
"prevdoc_detail_docname": "po_detail",
|
"prevdoc_detail_docname": "po_detail",
|
||||||
"prevdoc_docname": "purchase_order",
|
"prevdoc_docname": "purchase_order",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Purchase Taxes and Charges": {
|
"Purchase Taxes and Charges": {
|
||||||
"doctype": "Purchase Taxes and Charges",
|
"doctype": "Purchase Taxes and Charges",
|
||||||
"add_if_empty": True
|
"add_if_empty": True
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
@ -15,88 +15,88 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
|
||||||
|
|
||||||
pr = frappe.copy_doc(test_records[0]).insert()
|
pr = frappe.copy_doc(test_records[0]).insert()
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice,
|
self.assertRaises(frappe.ValidationError, make_purchase_invoice,
|
||||||
pr.name)
|
pr.name)
|
||||||
|
|
||||||
pr = frappe.get_doc("Purchase Receipt", pr.name)
|
pr = frappe.get_doc("Purchase Receipt", pr.name)
|
||||||
pr.submit()
|
pr.submit()
|
||||||
pi = make_purchase_invoice(pr.name)
|
pi = make_purchase_invoice(pr.name)
|
||||||
|
|
||||||
self.assertEquals(pi["doctype"], "Purchase Invoice")
|
self.assertEquals(pi.doctype, "Purchase Invoice")
|
||||||
self.assertEquals(len(pi.get("entries")), len(pr.get("purchase_receipt_details")))
|
self.assertEquals(len(pi.get("entries")), len(pr.get("purchase_receipt_details")))
|
||||||
|
|
||||||
# modify rate
|
# modify rate
|
||||||
pi.get("entries")[0]["rate"] = 200
|
pi.get("entries")[0].rate = 200
|
||||||
self.assertRaises(frappe.ValidationError, frappe.get_doc(pi).submit)
|
self.assertRaises(frappe.ValidationError, frappe.get_doc(pi).submit)
|
||||||
|
|
||||||
def test_purchase_receipt_no_gl_entry(self):
|
def test_purchase_receipt_no_gl_entry(self):
|
||||||
self._clear_stock_account_balance()
|
self._clear_stock_account_balance()
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
pr = frappe.copy_doc(test_records[0])
|
pr = frappe.copy_doc(test_records[0])
|
||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
stock_value, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
|
stock_value, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
|
||||||
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name,
|
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name,
|
||||||
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
||||||
["stock_value", "stock_value_difference"])
|
["stock_value", "stock_value_difference"])
|
||||||
self.assertEqual(stock_value, 375)
|
self.assertEqual(stock_value, 375)
|
||||||
self.assertEqual(stock_value_difference, 375)
|
self.assertEqual(stock_value_difference, 375)
|
||||||
|
|
||||||
bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "stock_value")
|
"warehouse": "_Test Warehouse - _TC"}, "stock_value")
|
||||||
self.assertEqual(bin_stock_value, 375)
|
self.assertEqual(bin_stock_value, 375)
|
||||||
|
|
||||||
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
|
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
|
||||||
|
|
||||||
def test_purchase_receipt_gl_entry(self):
|
def test_purchase_receipt_gl_entry(self):
|
||||||
self._clear_stock_account_balance()
|
self._clear_stock_account_balance()
|
||||||
|
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
||||||
|
|
||||||
pr = frappe.copy_doc(test_records[0])
|
pr = frappe.copy_doc(test_records[0])
|
||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
||||||
|
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
stock_in_hand_account = frappe.db.get_value("Account",
|
stock_in_hand_account = frappe.db.get_value("Account",
|
||||||
{"master_name": pr.get("purchase_receipt_details")[0].warehouse})
|
{"master_name": pr.get("purchase_receipt_details")[0].warehouse})
|
||||||
fixed_asset_account = frappe.db.get_value("Account",
|
fixed_asset_account = frappe.db.get_value("Account",
|
||||||
{"master_name": pr.get("purchase_receipt_details")[1].warehouse})
|
{"master_name": pr.get("purchase_receipt_details")[1].warehouse})
|
||||||
|
|
||||||
expected_values = {
|
expected_values = {
|
||||||
stock_in_hand_account: [375.0, 0.0],
|
stock_in_hand_account: [375.0, 0.0],
|
||||||
fixed_asset_account: [375.0, 0.0],
|
fixed_asset_account: [375.0, 0.0],
|
||||||
"Stock Received But Not Billed - _TC": [0.0, 750.0]
|
"Stock Received But Not Billed - _TC": [0.0, 750.0]
|
||||||
}
|
}
|
||||||
|
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
self.assertEquals(expected_values[gle.account][0], gle.debit)
|
self.assertEquals(expected_values[gle.account][0], gle.debit)
|
||||||
self.assertEquals(expected_values[gle.account][1], gle.credit)
|
self.assertEquals(expected_values[gle.account][1], gle.credit)
|
||||||
|
|
||||||
pr.cancel()
|
pr.cancel()
|
||||||
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
|
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
|
||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def _clear_stock_account_balance(self):
|
def _clear_stock_account_balance(self):
|
||||||
frappe.db.sql("delete from `tabStock Ledger Entry`")
|
frappe.db.sql("delete from `tabStock Ledger Entry`")
|
||||||
frappe.db.sql("""delete from `tabBin`""")
|
frappe.db.sql("""delete from `tabBin`""")
|
||||||
frappe.db.sql("""delete from `tabGL Entry`""")
|
frappe.db.sql("""delete from `tabGL Entry`""")
|
||||||
|
|
||||||
def test_subcontracting(self):
|
def test_subcontracting(self):
|
||||||
pr = frappe.copy_doc(test_records[1])
|
pr = frappe.copy_doc(test_records[1])
|
||||||
pr.run_method("calculate_taxes_and_totals")
|
pr.run_method("calculate_taxes_and_totals")
|
||||||
pr.insert()
|
pr.insert()
|
||||||
|
|
||||||
self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 70000.0)
|
self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 70000.0)
|
||||||
self.assertEquals(len(pr.get("pr_raw_material_details")), 2)
|
self.assertEquals(len(pr.get("pr_raw_material_details")), 2)
|
||||||
|
|
||||||
def test_serial_no_supplier(self):
|
def test_serial_no_supplier(self):
|
||||||
pr = frappe.copy_doc(test_records[0])
|
pr = frappe.copy_doc(test_records[0])
|
||||||
pr.get("purchase_receipt_details")[0].item_code = "_Test Serialized Item With Series"
|
pr.get("purchase_receipt_details")[0].item_code = "_Test Serialized Item With Series"
|
||||||
@ -104,30 +104,30 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
pr.get("purchase_receipt_details")[0].received_qty = 1
|
pr.get("purchase_receipt_details")[0].received_qty = 1
|
||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
self.assertEquals(frappe.db.get_value("Serial No", pr.get("purchase_receipt_details")[0].serial_no,
|
self.assertEquals(frappe.db.get_value("Serial No", pr.get("purchase_receipt_details")[0].serial_no,
|
||||||
"supplier"), pr.supplier)
|
"supplier"), pr.supplier)
|
||||||
|
|
||||||
return pr
|
return pr
|
||||||
|
|
||||||
def test_serial_no_cancel(self):
|
def test_serial_no_cancel(self):
|
||||||
pr = self.test_serial_no_supplier()
|
pr = self.test_serial_no_supplier()
|
||||||
pr.cancel()
|
pr.cancel()
|
||||||
|
|
||||||
self.assertFalse(frappe.db.get_value("Serial No", pr.get("purchase_receipt_details")[0].serial_no,
|
self.assertFalse(frappe.db.get_value("Serial No", pr.get("purchase_receipt_details")[0].serial_no,
|
||||||
"warehouse"))
|
"warehouse"))
|
||||||
|
|
||||||
def get_gl_entries(voucher_type, voucher_no):
|
def get_gl_entries(voucher_type, voucher_no):
|
||||||
return frappe.db.sql("""select account, debit, credit
|
return frappe.db.sql("""select account, debit, credit
|
||||||
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
|
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
|
||||||
order by account desc""", (voucher_type, voucher_no), as_dict=1)
|
order by account desc""", (voucher_type, voucher_no), as_dict=1)
|
||||||
|
|
||||||
def set_perpetual_inventory(enable=1):
|
def set_perpetual_inventory(enable=1):
|
||||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||||
accounts_settings.auto_accounting_for_stock = enable
|
accounts_settings.auto_accounting_for_stock = enable
|
||||||
accounts_settings.save()
|
accounts_settings.save()
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["BOM"]
|
test_dependencies = ["BOM"]
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Purchase Receipt')
|
test_records = frappe.get_test_records('Purchase Receipt')
|
||||||
|
@ -407,14 +407,14 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice, make_delivery_note
|
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice, make_delivery_note
|
||||||
|
|
||||||
actual_qty_0 = self._get_actual_qty()
|
actual_qty_0 = self._get_actual_qty()
|
||||||
|
|
||||||
so = frappe.copy_doc(sales_order_test_records[0])
|
so = frappe.copy_doc(sales_order_test_records[0])
|
||||||
so.get("sales_order_details")[0].item_code = item_code
|
so.get("sales_order_details")[0].item_code = item_code
|
||||||
so.get("sales_order_details")[0].qty = 5.0
|
so.get("sales_order_details")[0].qty = 5.0
|
||||||
so.insert()
|
so.insert()
|
||||||
so.submit()
|
so.submit()
|
||||||
dn_doc = make_delivery_note(so.name)
|
|
||||||
|
|
||||||
dn = frappe.get_doc(dn_doc)
|
dn = make_delivery_note(so.name)
|
||||||
dn.status = "Draft"
|
dn.status = "Draft"
|
||||||
dn.posting_date = so.delivery_date
|
dn.posting_date = so.delivery_date
|
||||||
dn.insert()
|
dn.insert()
|
||||||
@ -423,9 +423,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
actual_qty_1 = self._get_actual_qty()
|
actual_qty_1 = self._get_actual_qty()
|
||||||
self.assertEquals(actual_qty_0 - delivered_qty, actual_qty_1)
|
self.assertEquals(actual_qty_0 - delivered_qty, actual_qty_1)
|
||||||
|
|
||||||
si_doc = make_sales_invoice(so.name)
|
si = make_sales_invoice(so.name)
|
||||||
|
|
||||||
si = frappe.get_doc(si_doc)
|
|
||||||
si.posting_date = dn.posting_date
|
si.posting_date = dn.posting_date
|
||||||
si.debit_to = "_Test Customer - _TC"
|
si.debit_to = "_Test Customer - _TC"
|
||||||
for d in si.get("entries"):
|
for d in si.get("entries"):
|
||||||
|
@ -14,15 +14,15 @@ from erpnext.accounts.utils import get_fiscal_year, get_stock_and_account_differ
|
|||||||
class TestStockReconciliation(unittest.TestCase):
|
class TestStockReconciliation(unittest.TestCase):
|
||||||
def test_reco_for_fifo(self):
|
def test_reco_for_fifo(self):
|
||||||
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
||||||
# [[qty, valuation_rate, posting_date,
|
# [[qty, valuation_rate, posting_date,
|
||||||
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
|
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
|
||||||
input_data = [
|
input_data = [
|
||||||
[50, 1000, "2012-12-26", "12:00", 50000, 45, 48000],
|
[50, 1000, "2012-12-26", "12:00", 50000, 45, 48000],
|
||||||
[5, 1000, "2012-12-26", "12:00", 5000, 0, 0],
|
[5, 1000, "2012-12-26", "12:00", 5000, 0, 0],
|
||||||
[15, 1000, "2012-12-26", "12:00", 15000, 10, 12000],
|
[15, 1000, "2012-12-26", "12:00", 15000, 10, 12000],
|
||||||
[25, 900, "2012-12-26", "12:00", 22500, 20, 22500],
|
[25, 900, "2012-12-26", "12:00", 22500, 20, 22500],
|
||||||
[20, 500, "2012-12-26", "12:00", 10000, 15, 18000],
|
[20, 500, "2012-12-26", "12:00", 10000, 15, 18000],
|
||||||
[50, 1000, "2013-01-01", "12:00", 50000, 65, 68000],
|
[50, 1000, "2013-01-01", "12:00", 50000, 65, 68000],
|
||||||
[5, 1000, "2013-01-01", "12:00", 5000, 20, 23000],
|
[5, 1000, "2013-01-01", "12:00", 5000, 20, 23000],
|
||||||
["", 1000, "2012-12-26", "12:05", 15000, 10, 12000],
|
["", 1000, "2012-12-26", "12:05", 15000, 10, 12000],
|
||||||
[20, "", "2012-12-26", "12:05", 16000, 15, 18000],
|
[20, "", "2012-12-26", "12:05", 16000, 15, 18000],
|
||||||
@ -30,142 +30,142 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
||||||
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in input_data:
|
for d in input_data:
|
||||||
self.cleanup_data()
|
self.cleanup_data()
|
||||||
self.insert_existing_sle("FIFO")
|
self.insert_existing_sle("FIFO")
|
||||||
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
||||||
|
|
||||||
# check stock value
|
# check stock value
|
||||||
res = frappe.db.sql("""select stock_value from `tabStock Ledger Entry`
|
res = frappe.db.sql("""select stock_value from `tabStock Ledger Entry`
|
||||||
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'
|
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'
|
||||||
and posting_date = %s and posting_time = %s order by name desc limit 1""",
|
and posting_date = %s and posting_time = %s order by name desc limit 1""",
|
||||||
(d[2], d[3]))
|
(d[2], d[3]))
|
||||||
self.assertEqual(res and flt(res[0][0]) or 0, d[4])
|
self.assertEqual(res and flt(res[0][0]) or 0, d[4])
|
||||||
|
|
||||||
# check bin qty and stock value
|
# check bin qty and stock value
|
||||||
bin = frappe.db.sql("""select actual_qty, stock_value from `tabBin`
|
bin = frappe.db.sql("""select actual_qty, stock_value from `tabBin`
|
||||||
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'""")
|
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'""")
|
||||||
|
|
||||||
self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1])] or [], [d[5], d[6]])
|
self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1])] or [], [d[5], d[6]])
|
||||||
|
|
||||||
# no gl entries
|
# no gl entries
|
||||||
gl_entries = frappe.db.sql("""select name from `tabGL Entry`
|
gl_entries = frappe.db.sql("""select name from `tabGL Entry`
|
||||||
where voucher_type = 'Stock Reconciliation' and voucher_no = %s""",
|
where voucher_type = 'Stock Reconciliation' and voucher_no = %s""",
|
||||||
stock_reco.name)
|
stock_reco.name)
|
||||||
self.assertFalse(gl_entries)
|
self.assertFalse(gl_entries)
|
||||||
|
|
||||||
|
|
||||||
def test_reco_for_moving_average(self):
|
def test_reco_for_moving_average(self):
|
||||||
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
||||||
# [[qty, valuation_rate, posting_date,
|
# [[qty, valuation_rate, posting_date,
|
||||||
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
|
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
|
||||||
input_data = [
|
input_data = [
|
||||||
[50, 1000, "2012-12-26", "12:00", 50000, 45, 48000],
|
[50, 1000, "2012-12-26", "12:00", 50000, 45, 48000],
|
||||||
[5, 1000, "2012-12-26", "12:00", 5000, 0, 0],
|
[5, 1000, "2012-12-26", "12:00", 5000, 0, 0],
|
||||||
[15, 1000, "2012-12-26", "12:00", 15000, 10, 12000],
|
[15, 1000, "2012-12-26", "12:00", 15000, 10, 12000],
|
||||||
[25, 900, "2012-12-26", "12:00", 22500, 20, 22500],
|
[25, 900, "2012-12-26", "12:00", 22500, 20, 22500],
|
||||||
[20, 500, "2012-12-26", "12:00", 10000, 15, 18000],
|
[20, 500, "2012-12-26", "12:00", 10000, 15, 18000],
|
||||||
[50, 1000, "2013-01-01", "12:00", 50000, 65, 68000],
|
[50, 1000, "2013-01-01", "12:00", 50000, 65, 68000],
|
||||||
[5, 1000, "2013-01-01", "12:00", 5000, 20, 23000],
|
[5, 1000, "2013-01-01", "12:00", 5000, 20, 23000],
|
||||||
["", 1000, "2012-12-26", "12:05", 15000, 10, 12000],
|
["", 1000, "2012-12-26", "12:05", 15000, 10, 12000],
|
||||||
[20, "", "2012-12-26", "12:05", 18000, 15, 18000],
|
[20, "", "2012-12-26", "12:05", 18000, 15, 18000],
|
||||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 6000],
|
[10, 2000, "2012-12-26", "12:10", 20000, 5, 6000],
|
||||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
||||||
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in input_data:
|
for d in input_data:
|
||||||
self.cleanup_data()
|
self.cleanup_data()
|
||||||
self.insert_existing_sle("Moving Average")
|
self.insert_existing_sle("Moving Average")
|
||||||
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
||||||
|
|
||||||
# check stock value in sle
|
# check stock value in sle
|
||||||
res = frappe.db.sql("""select stock_value from `tabStock Ledger Entry`
|
res = frappe.db.sql("""select stock_value from `tabStock Ledger Entry`
|
||||||
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'
|
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'
|
||||||
and posting_date = %s and posting_time = %s order by name desc limit 1""",
|
and posting_date = %s and posting_time = %s order by name desc limit 1""",
|
||||||
(d[2], d[3]))
|
(d[2], d[3]))
|
||||||
|
|
||||||
self.assertEqual(res and flt(res[0][0], 4) or 0, d[4])
|
self.assertEqual(res and flt(res[0][0], 4) or 0, d[4])
|
||||||
|
|
||||||
# bin qty and stock value
|
# bin qty and stock value
|
||||||
bin = frappe.db.sql("""select actual_qty, stock_value from `tabBin`
|
bin = frappe.db.sql("""select actual_qty, stock_value from `tabBin`
|
||||||
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'""")
|
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'""")
|
||||||
|
|
||||||
self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1], 4)] or [],
|
self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1], 4)] or [],
|
||||||
[flt(d[5]), flt(d[6])])
|
[flt(d[5]), flt(d[6])])
|
||||||
|
|
||||||
# no gl entries
|
# no gl entries
|
||||||
gl_entries = frappe.db.sql("""select name from `tabGL Entry`
|
gl_entries = frappe.db.sql("""select name from `tabGL Entry`
|
||||||
where voucher_type = 'Stock Reconciliation' and voucher_no = %s""",
|
where voucher_type = 'Stock Reconciliation' and voucher_no = %s""",
|
||||||
stock_reco.name)
|
stock_reco.name)
|
||||||
self.assertFalse(gl_entries)
|
self.assertFalse(gl_entries)
|
||||||
|
|
||||||
def test_reco_fifo_gl_entries(self):
|
def test_reco_fifo_gl_entries(self):
|
||||||
frappe.defaults.set_global_default("auto_accounting_for_stock", 1)
|
frappe.defaults.set_global_default("auto_accounting_for_stock", 1)
|
||||||
|
|
||||||
# [[qty, valuation_rate, posting_date, posting_time, stock_in_hand_debit]]
|
# [[qty, valuation_rate, posting_date, posting_time, stock_in_hand_debit]]
|
||||||
input_data = [
|
input_data = [
|
||||||
[50, 1000, "2012-12-26", "12:00"],
|
[50, 1000, "2012-12-26", "12:00"],
|
||||||
[5, 1000, "2012-12-26", "12:00"],
|
[5, 1000, "2012-12-26", "12:00"],
|
||||||
[15, 1000, "2012-12-26", "12:00"],
|
[15, 1000, "2012-12-26", "12:00"],
|
||||||
[25, 900, "2012-12-26", "12:00"],
|
[25, 900, "2012-12-26", "12:00"],
|
||||||
[20, 500, "2012-12-26", "12:00"],
|
[20, 500, "2012-12-26", "12:00"],
|
||||||
["", 1000, "2012-12-26", "12:05"],
|
["", 1000, "2012-12-26", "12:05"],
|
||||||
[20, "", "2012-12-26", "12:05"],
|
[20, "", "2012-12-26", "12:05"],
|
||||||
[10, 2000, "2012-12-26", "12:10"],
|
[10, 2000, "2012-12-26", "12:10"],
|
||||||
[0, "", "2012-12-26", "12:10"],
|
[0, "", "2012-12-26", "12:10"],
|
||||||
[50, 1000, "2013-01-01", "12:00"],
|
[50, 1000, "2013-01-01", "12:00"],
|
||||||
[5, 1000, "2013-01-01", "12:00"],
|
[5, 1000, "2013-01-01", "12:00"],
|
||||||
[1, 1000, "2012-12-01", "00:00"],
|
[1, 1000, "2012-12-01", "00:00"],
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in input_data:
|
for d in input_data:
|
||||||
self.cleanup_data()
|
self.cleanup_data()
|
||||||
self.insert_existing_sle("FIFO")
|
self.insert_existing_sle("FIFO")
|
||||||
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
|
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
|
||||||
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
||||||
|
|
||||||
|
|
||||||
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
|
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
|
||||||
|
|
||||||
stock_reco.cancel()
|
stock_reco.cancel()
|
||||||
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
|
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
|
||||||
|
|
||||||
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
||||||
|
|
||||||
def test_reco_moving_average_gl_entries(self):
|
def test_reco_moving_average_gl_entries(self):
|
||||||
frappe.defaults.set_global_default("auto_accounting_for_stock", 1)
|
frappe.defaults.set_global_default("auto_accounting_for_stock", 1)
|
||||||
|
|
||||||
# [[qty, valuation_rate, posting_date,
|
# [[qty, valuation_rate, posting_date,
|
||||||
# posting_time, stock_in_hand_debit]]
|
# posting_time, stock_in_hand_debit]]
|
||||||
input_data = [
|
input_data = [
|
||||||
[50, 1000, "2012-12-26", "12:00", 36500],
|
[50, 1000, "2012-12-26", "12:00", 36500],
|
||||||
[5, 1000, "2012-12-26", "12:00", -8500],
|
[5, 1000, "2012-12-26", "12:00", -8500],
|
||||||
[15, 1000, "2012-12-26", "12:00", 1500],
|
[15, 1000, "2012-12-26", "12:00", 1500],
|
||||||
[25, 900, "2012-12-26", "12:00", 9000],
|
[25, 900, "2012-12-26", "12:00", 9000],
|
||||||
[20, 500, "2012-12-26", "12:00", -3500],
|
[20, 500, "2012-12-26", "12:00", -3500],
|
||||||
["", 1000, "2012-12-26", "12:05", 1500],
|
["", 1000, "2012-12-26", "12:05", 1500],
|
||||||
[20, "", "2012-12-26", "12:05", 4500],
|
[20, "", "2012-12-26", "12:05", 4500],
|
||||||
[10, 2000, "2012-12-26", "12:10", 6500],
|
[10, 2000, "2012-12-26", "12:10", 6500],
|
||||||
[0, "", "2012-12-26", "12:10", -13500],
|
[0, "", "2012-12-26", "12:10", -13500],
|
||||||
[50, 1000, "2013-01-01", "12:00", 50000],
|
[50, 1000, "2013-01-01", "12:00", 50000],
|
||||||
[5, 1000, "2013-01-01", "12:00", 5000],
|
[5, 1000, "2013-01-01", "12:00", 5000],
|
||||||
[1, 1000, "2012-12-01", "00:00", 1000],
|
[1, 1000, "2012-12-01", "00:00", 1000],
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in input_data:
|
for d in input_data:
|
||||||
self.cleanup_data()
|
self.cleanup_data()
|
||||||
self.insert_existing_sle("Moving Average")
|
self.insert_existing_sle("Moving Average")
|
||||||
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
|
||||||
self.assertFalse(get_stock_and_account_difference(["_Test Warehouse - _TC"]))
|
self.assertFalse(get_stock_and_account_difference(["_Test Warehouse - _TC"]))
|
||||||
|
|
||||||
# cancel
|
# cancel
|
||||||
stock_reco.cancel()
|
stock_reco.cancel()
|
||||||
self.assertFalse(get_stock_and_account_difference(["_Test Warehouse - _TC"]))
|
self.assertFalse(get_stock_and_account_difference(["_Test Warehouse - _TC"]))
|
||||||
|
|
||||||
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
frappe.defaults.set_global_default("auto_accounting_for_stock", 0)
|
||||||
|
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
frappe.db.sql("delete from `tabStock Ledger Entry`")
|
frappe.db.sql("delete from `tabStock Ledger Entry`")
|
||||||
frappe.db.sql("delete from tabBin")
|
frappe.db.sql("delete from tabBin")
|
||||||
frappe.db.sql("delete from `tabGL Entry`")
|
frappe.db.sql("delete from `tabGL Entry`")
|
||||||
|
|
||||||
def submit_stock_reconciliation(self, qty, rate, posting_date, posting_time):
|
def submit_stock_reconciliation(self, qty, rate, posting_date, posting_time):
|
||||||
stock_reco = frappe.get_doc({
|
stock_reco = frappe.get_doc({
|
||||||
"doctype": "Stock Reconciliation",
|
"doctype": "Stock Reconciliation",
|
||||||
@ -191,40 +191,40 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
stock_reco.insert()
|
stock_reco.insert()
|
||||||
stock_reco.submit()
|
stock_reco.submit()
|
||||||
return stock_reco
|
return stock_reco
|
||||||
|
|
||||||
def insert_existing_sle(self, valuation_method):
|
def insert_existing_sle(self, valuation_method):
|
||||||
frappe.db.set_value("Item", "_Test Item", "valuation_method", valuation_method)
|
frappe.db.set_value("Item", "_Test Item", "valuation_method", valuation_method)
|
||||||
frappe.db.set_default("allow_negative_stock", 1)
|
frappe.db.set_default("allow_negative_stock", 1)
|
||||||
|
|
||||||
stock_entry = [
|
stock_entry = {
|
||||||
{
|
"company": "_Test Company",
|
||||||
"company": "_Test Company",
|
"doctype": "Stock Entry",
|
||||||
"doctype": "Stock Entry",
|
"posting_date": "2012-12-12",
|
||||||
"posting_date": "2012-12-12",
|
"posting_time": "01:00",
|
||||||
"posting_time": "01:00",
|
"purpose": "Material Receipt",
|
||||||
"purpose": "Material Receipt",
|
"fiscal_year": "_Test Fiscal Year 2012",
|
||||||
"fiscal_year": "_Test Fiscal Year 2012",
|
"mtn_details": [
|
||||||
},
|
{
|
||||||
{
|
"conversion_factor": 1.0,
|
||||||
"conversion_factor": 1.0,
|
"doctype": "Stock Entry Detail",
|
||||||
"doctype": "Stock Entry Detail",
|
"item_code": "_Test Item",
|
||||||
"item_code": "_Test Item",
|
"parentfield": "mtn_details",
|
||||||
"parentfield": "mtn_details",
|
"incoming_rate": 1000,
|
||||||
"incoming_rate": 1000,
|
"qty": 20.0,
|
||||||
"qty": 20.0,
|
"stock_uom": "_Test UOM",
|
||||||
"stock_uom": "_Test UOM",
|
"transfer_qty": 20.0,
|
||||||
"transfer_qty": 20.0,
|
"uom": "_Test UOM",
|
||||||
"uom": "_Test UOM",
|
"t_warehouse": "_Test Warehouse - _TC",
|
||||||
"t_warehouse": "_Test Warehouse - _TC",
|
"expense_account": "Stock Adjustment - _TC",
|
||||||
"expense_account": "Stock Adjustment - _TC",
|
"cost_center": "_Test Cost Center - _TC"
|
||||||
"cost_center": "_Test Cost Center - _TC"
|
}
|
||||||
},
|
]
|
||||||
]
|
}
|
||||||
|
|
||||||
pr = frappe.copy_doc(stock_entry)
|
pr = frappe.copy_doc(stock_entry)
|
||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
pr1 = frappe.copy_doc(stock_entry)
|
pr1 = frappe.copy_doc(stock_entry)
|
||||||
pr1.posting_date = "2012-12-15"
|
pr1.posting_date = "2012-12-15"
|
||||||
pr1.posting_time = "02:00"
|
pr1.posting_time = "02:00"
|
||||||
@ -233,7 +233,7 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
pr1.get("mtn_details")[0].incoming_rate = 700
|
pr1.get("mtn_details")[0].incoming_rate = 700
|
||||||
pr1.insert()
|
pr1.insert()
|
||||||
pr1.submit()
|
pr1.submit()
|
||||||
|
|
||||||
pr2 = frappe.copy_doc(stock_entry)
|
pr2 = frappe.copy_doc(stock_entry)
|
||||||
pr2.posting_date = "2012-12-25"
|
pr2.posting_date = "2012-12-25"
|
||||||
pr2.posting_time = "03:00"
|
pr2.posting_time = "03:00"
|
||||||
@ -245,7 +245,7 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
pr2.get("mtn_details")[0].incoming_rate = 0
|
pr2.get("mtn_details")[0].incoming_rate = 0
|
||||||
pr2.insert()
|
pr2.insert()
|
||||||
pr2.submit()
|
pr2.submit()
|
||||||
|
|
||||||
pr3 = frappe.copy_doc(stock_entry)
|
pr3 = frappe.copy_doc(stock_entry)
|
||||||
pr3.posting_date = "2012-12-31"
|
pr3.posting_date = "2012-12-31"
|
||||||
pr3.posting_time = "08:00"
|
pr3.posting_time = "08:00"
|
||||||
@ -257,8 +257,8 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
pr3.get("mtn_details")[0].incoming_rate = 0
|
pr3.get("mtn_details")[0].incoming_rate = 0
|
||||||
pr3.insert()
|
pr3.insert()
|
||||||
pr3.submit()
|
pr3.submit()
|
||||||
|
|
||||||
|
|
||||||
pr4 = frappe.copy_doc(stock_entry)
|
pr4 = frappe.copy_doc(stock_entry)
|
||||||
pr4.posting_date = "2013-01-05"
|
pr4.posting_date = "2013-01-05"
|
||||||
pr4.fiscal_year = "_Test Fiscal Year 2013"
|
pr4.fiscal_year = "_Test Fiscal Year 2013"
|
||||||
@ -268,6 +268,6 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
pr4.get("mtn_details")[0].incoming_rate = 1200
|
pr4.get("mtn_details")[0].incoming_rate = 1200
|
||||||
pr4.insert()
|
pr4.insert()
|
||||||
pr4.submit()
|
pr4.submit()
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Item", "Warehouse"]
|
test_dependencies = ["Item", "Warehouse"]
|
||||||
|
@ -7,36 +7,36 @@ class TestNewsletter(unittest.TestCase):
|
|||||||
def test_get_recipients_lead(self):
|
def test_get_recipients_lead(self):
|
||||||
w = frappe.get_doc(test_records[0])
|
w = frappe.get_doc(test_records[0])
|
||||||
w.insert()
|
w.insert()
|
||||||
self.assertTrue("test_lead@example.com" in w.controller.get_recipients())
|
self.assertTrue("test_lead@example.com" in w.get_recipients())
|
||||||
frappe.db.sql("""delete from `tabBulk Email`""")
|
frappe.db.sql("""delete from `tabBulk Email`""")
|
||||||
w.controller.send_emails()
|
w.send_emails()
|
||||||
self.assertTrue(frappe.db.get_value("Bulk Email", {"recipient": "test_lead@example.com"}))
|
self.assertTrue(frappe.db.get_value("Bulk Email", {"recipient": "test_lead@example.com"}))
|
||||||
|
|
||||||
def test_get_recipients_lead_by_status(self):
|
def test_get_recipients_lead_by_status(self):
|
||||||
w = frappe.get_doc(test_records[0])
|
w = frappe.get_doc(test_records[0])
|
||||||
w.lead_status="Converted"
|
w.lead_status="Converted"
|
||||||
w.insert()
|
w.insert()
|
||||||
self.assertTrue("test_lead3@example.com" in w.controller.get_recipients())
|
self.assertTrue("test_lead3@example.com" in w.get_recipients())
|
||||||
|
|
||||||
def test_get_recipients_contact_customer(self):
|
def test_get_recipients_contact_customer(self):
|
||||||
w = frappe.get_doc(test_records[1])
|
w = frappe.get_doc(test_records[1])
|
||||||
w.insert()
|
w.insert()
|
||||||
self.assertTrue("test_contact_customer@example.com" in w.controller.get_recipients())
|
self.assertTrue("test_contact_customer@example.com" in w.get_recipients())
|
||||||
|
|
||||||
def test_get_recipients_contact_supplier(self):
|
def test_get_recipients_contact_supplier(self):
|
||||||
w = frappe.get_doc(test_records[1])
|
w = frappe.get_doc(test_records[1])
|
||||||
w.contact_type="Supplier"
|
w.contact_type="Supplier"
|
||||||
w.insert()
|
w.insert()
|
||||||
self.assertTrue("test_contact_supplier@example.com" in w.controller.get_recipients())
|
self.assertTrue("test_contact_supplier@example.com" in w.get_recipients())
|
||||||
|
|
||||||
def test_get_recipients_custom(self):
|
def test_get_recipients_custom(self):
|
||||||
w = frappe.get_doc(test_records[2])
|
w = frappe.get_doc(test_records[2])
|
||||||
w.insert()
|
w.insert()
|
||||||
self.assertTrue("test_custom2@example.com" in w.controller.get_recipients())
|
self.assertTrue("test_custom2@example.com" in w.get_recipients())
|
||||||
self.assertTrue(frappe.db.get("Lead",
|
self.assertTrue(frappe.db.get("Lead",
|
||||||
{"email_id": "test_custom2@example.com"}))
|
{"email_id": "test_custom2@example.com"}))
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Lead", "Contact"]
|
test_dependencies = ["Lead", "Contact"]
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Newsletter')
|
test_records = frappe.get_test_records('Newsletter')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user