mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-25 16:04:46 +00:00
Merge branch 'master' of github.com:webnotes/erpnext
This commit is contained in:
@@ -18,11 +18,7 @@ from __future__ import unicode_literals
|
|||||||
import webnotes
|
import webnotes
|
||||||
from webnotes.utils import flt, getdate
|
from webnotes.utils import flt, getdate
|
||||||
from webnotes.model.doc import make_autoname
|
from webnotes.model.doc import make_autoname
|
||||||
from webnotes.model.wrapper import getlist, copy_doclist
|
from webnotes.model.wrapper import getlist
|
||||||
from webnotes import msgprint
|
|
||||||
|
|
||||||
sql = webnotes.conn.sql
|
|
||||||
|
|
||||||
|
|
||||||
class DocType:
|
class DocType:
|
||||||
def __init__(self,d,dl):
|
def __init__(self,d,dl):
|
||||||
@@ -31,53 +27,57 @@ class DocType:
|
|||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.doc.name = make_autoname(self.doc.naming_series + '.#####')
|
self.doc.name = make_autoname(self.doc.naming_series + '.#####')
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""Validate invoice that c-form is applicable
|
||||||
|
and no other c-form is received for that"""
|
||||||
|
|
||||||
|
for d in getlist(self.doclist, 'invoice_details'):
|
||||||
|
inv = webnotes.conn.sql("""select c_form_applicable, c_form_no from
|
||||||
|
`tabSales Invoice` where name = %s""", d.invoice_no)
|
||||||
|
|
||||||
|
if not inv:
|
||||||
|
webnotes.msgprint("Invoice: %s is not exists in the system, please check." %
|
||||||
|
d.invoice_no, raise_exception=1)
|
||||||
|
|
||||||
|
elif inv[0][0] != 'Yes':
|
||||||
|
webnotes.msgprint("C-form is not applicable for Invoice: %s" %
|
||||||
|
d.invoice_no, raise_exception=1)
|
||||||
|
|
||||||
|
elif inv[0][1] and inv[0][1] != self.doc.name:
|
||||||
|
webnotes.msgprint("""Invoice %s is tagged in another C-form: %s.
|
||||||
|
If you want to change C-form no for this invoice,
|
||||||
|
please remove invoice no from the previous c-form and then try again""" %
|
||||||
|
(d.invoice_no, inv[0][1]), raise_exception=1)
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
""" Update C-Form No on invoices"""
|
""" Update C-Form No on invoices"""
|
||||||
|
inv = [d.invoice_no for d in getlist(self.doclist, 'invoice_details')]
|
||||||
|
if inv:
|
||||||
|
webnotes.conn.sql("""update `tabSales Invoice` set c_form_no=%s, modified=%s
|
||||||
|
where name in (%s)""" % ('%s', '%s', ', '.join(['%s'] * len(inv))),
|
||||||
|
tuple([self.doc.name, self.doc.modified] + inv))
|
||||||
|
|
||||||
if len(getlist(self.doclist, 'invoice_details')):
|
webnotes.conn.sql("""update `tabSales Invoice` set c_form_no = '', modified = %s
|
||||||
inv = "'" + "', '".join([d.invoice_no for d in getlist(self.doclist, 'invoice_details')]) + "'"
|
where name not in (%s) and ifnull(c_form_no, '') = %s""" %
|
||||||
sql("""update `tabSales Invoice` set c_form_no = '%s', modified ='%s'
|
('%s', ', '.join(['%s']*len(inv)), '%s'),
|
||||||
where name in (%s)"""%(self.doc.name, self.doc.modified, inv))
|
tuple([self.doc.modified] + inv + [self.doc.name]))
|
||||||
sql("""update `tabSales Invoice` set c_form_no = '', modified = %s where name not
|
|
||||||
in (%s) and ifnull(c_form_no, '') = %s""", (self.doc.modified, self.doc.name, inv))
|
|
||||||
else:
|
else:
|
||||||
msgprint("Please enter atleast 1 invoice in the table below", raise_exception=1)
|
webnotes.msgprint("Please enter atleast 1 invoice in the table", raise_exception=1)
|
||||||
|
|
||||||
self.calculate_total_invoiced_amount()
|
self.set_total_invoiced_amount()
|
||||||
|
|
||||||
def calculate_total_invoiced_amount(self):
|
def set_total_invoiced_amount(self):
|
||||||
total = 0
|
total = sum([flt(d.total) for d in getlist(self.doclist, 'invoice_details')])
|
||||||
for d in getlist(self.doclist, 'invoice_details'):
|
|
||||||
total += flt(d.grand_total)
|
|
||||||
webnotes.conn.set(self.doc, 'total_invoiced_amount', total)
|
webnotes.conn.set(self.doc, 'total_invoiced_amount', total)
|
||||||
|
|
||||||
|
|
||||||
def get_invoice_details(self, invoice_no):
|
def get_invoice_details(self, invoice_no):
|
||||||
""" Pull details from invoices for referrence """
|
""" Pull details from invoices for referrence """
|
||||||
|
|
||||||
inv = sql("""select posting_date, territory, net_total, grand_total from
|
inv = webnotes.conn.sql("""select posting_date, territory, net_total, grand_total
|
||||||
`tabSales Invoice` where name = %s""", invoice_no)
|
from `tabSales Invoice` where name = %s""", invoice_no)
|
||||||
ret = {
|
return {
|
||||||
'invoice_date' : inv and getdate(inv[0][0]).strftime('%Y-%m-%d') or '',
|
'invoice_date' : inv and getdate(inv[0][0]).strftime('%Y-%m-%d') or '',
|
||||||
'territory' : inv and inv[0][1] or '',
|
'territory' : inv and inv[0][1] or '',
|
||||||
'net_total' : inv and flt(inv[0][2]) or '',
|
'net_total' : inv and flt(inv[0][2]) or '',
|
||||||
'grand_total' : inv and flt(inv[0][3]) or ''
|
'grand_total' : inv and flt(inv[0][3]) or ''
|
||||||
}
|
}
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def validate_invoice(self):
|
|
||||||
"""Validate invoice that c-form is applicable and no other c-form is
|
|
||||||
received for that"""
|
|
||||||
|
|
||||||
for d in getlist(self.doclist, 'invoice_details'):
|
|
||||||
inv = sql("""select c_form_applicable, c_form_no from
|
|
||||||
`tabSales Invoice` where name = %s""", invoice_no)
|
|
||||||
if not inv:
|
|
||||||
msgprint("Invoice: %s is not exists in the system, please check." % d.invoice_no, raise_exception=1)
|
|
||||||
elif inv[0][0] != 'Yes':
|
|
||||||
msgprint("C-form is not applicable for Invoice: %s" % d.invoice_no, raise_exception=1)
|
|
||||||
elif inv[0][1] and inv[0][1] != self.doc.name:
|
|
||||||
msgprint("""Invoice %s is tagged in another C-form: %s. \nIf you want to change C-form no for this invoice,
|
|
||||||
please remove invoice no from the previous c-form and then try again""" % (d.invoice_no, inv[0][1]), raise_exception=1)
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
{
|
{
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"creation": "2012-12-20 18:14:55",
|
"creation": "2013-01-16 14:48:56",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"modified": "2012-12-24 19:37:34"
|
"modified": "2013-01-17 11:21:46"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import webnotes
|
import webnotes
|
||||||
from webnotes.utils import cstr, flt, nowdate, get_defaults
|
from webnotes.utils import cstr, flt, cint, nowdate, add_days
|
||||||
from webnotes.model.doc import addchild, Document
|
from webnotes.model.doc import addchild, Document
|
||||||
from webnotes.model.wrapper import getlist
|
from webnotes.model.wrapper import getlist
|
||||||
from webnotes.model.code import get_obj
|
from webnotes.model.code import get_obj
|
||||||
@@ -210,7 +210,7 @@ class DocType:
|
|||||||
"wip_warehouse" : "",
|
"wip_warehouse" : "",
|
||||||
"fg_warehouse" : "",
|
"fg_warehouse" : "",
|
||||||
"status" : "Draft",
|
"status" : "Draft",
|
||||||
"fiscal_year" : get_defaults()["fiscal_year"]
|
"fiscal_year" : webnotes.conn.get_default("fiscal_year")
|
||||||
}
|
}
|
||||||
return bom_dict, item_dict
|
return bom_dict, item_dict
|
||||||
|
|
||||||
@@ -239,18 +239,22 @@ class DocType:
|
|||||||
return self.get_csv()
|
return self.get_csv()
|
||||||
|
|
||||||
def get_raw_materials(self, bom_dict):
|
def get_raw_materials(self, bom_dict):
|
||||||
""" Get raw materials considering sub-assembly items """
|
""" Get raw materials considering sub-assembly items
|
||||||
|
{
|
||||||
|
"item_code": [qty_required, description, stock_uom]
|
||||||
|
}
|
||||||
|
"""
|
||||||
for bom in bom_dict:
|
for bom in bom_dict:
|
||||||
if self.doc.use_multi_level_bom:
|
if self.doc.use_multi_level_bom:
|
||||||
# get all raw materials with sub assembly childs
|
# get all raw materials with sub assembly childs
|
||||||
fl_bom_items = sql("""
|
fl_bom_items = sql("""
|
||||||
select
|
select
|
||||||
item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,
|
item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,
|
||||||
description, stock_uom
|
description, stock_uom, min_order_qty
|
||||||
from
|
from
|
||||||
(
|
(
|
||||||
select distinct fb.name, fb.description, fb.item_code,
|
select distinct fb.name, fb.description, fb.item_code,
|
||||||
fb.qty_consumed_per_unit, fb.stock_uom
|
fb.qty_consumed_per_unit, fb.stock_uom, it.min_order_qty
|
||||||
from `tabBOM Explosion Item` fb,`tabItem` it
|
from `tabBOM Explosion Item` fb,`tabItem` it
|
||||||
where it.name = fb.item_code
|
where it.name = fb.item_code
|
||||||
and ifnull(it.is_pro_applicable, 'No') = 'No'
|
and ifnull(it.is_pro_applicable, 'No') = 'No'
|
||||||
@@ -263,18 +267,20 @@ class DocType:
|
|||||||
# Get all raw materials considering SA items as raw materials,
|
# Get all raw materials considering SA items as raw materials,
|
||||||
# so no childs of SA items
|
# so no childs of SA items
|
||||||
fl_bom_items = sql("""
|
fl_bom_items = sql("""
|
||||||
select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s',
|
select bom_item.item_code,
|
||||||
description, stock_uom
|
ifnull(sum(bom_item.qty_consumed_per_unit), 0) * %s,
|
||||||
from `tabBOM Item`
|
bom_item.description, bom_item.stock_uom, item.min_order_qty
|
||||||
where parent = '%s' and docstatus < 2
|
from `tabBOM Item` bom_item, tabItem item
|
||||||
|
where bom_item.parent = %s and bom_item.docstatus < 2
|
||||||
|
and bom_item.item_code = item.name
|
||||||
group by item_code
|
group by item_code
|
||||||
""" % (flt(bom_dict[bom]), bom))
|
""", (flt(bom_dict[bom]), bom))
|
||||||
|
|
||||||
self.make_items_dict(fl_bom_items)
|
self.make_items_dict(fl_bom_items)
|
||||||
|
|
||||||
def make_items_dict(self, item_list):
|
def make_items_dict(self, item_list):
|
||||||
for i in item_list:
|
for i in item_list:
|
||||||
self.item_dict[i[0]] = [(flt(self.item_dict.get(i[0], [0])[0]) + flt(i[1])), i[2], i[3]]
|
self.item_dict[i[0]] = [(flt(self.item_dict.get(i[0], [0])[0]) + flt(i[1])),
|
||||||
|
i[2], i[3], i[4]]
|
||||||
|
|
||||||
|
|
||||||
def get_csv(self):
|
def get_csv(self):
|
||||||
@@ -292,3 +298,84 @@ class DocType:
|
|||||||
item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty])
|
item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty])
|
||||||
|
|
||||||
return item_list
|
return item_list
|
||||||
|
|
||||||
|
def raise_purchase_request(self):
|
||||||
|
"""
|
||||||
|
Raise Purchase Request if projected qty is less than qty required
|
||||||
|
Requested qty should be shortage qty considering minimum order qty
|
||||||
|
"""
|
||||||
|
if not self.doc.purchase_request_for_warehouse:
|
||||||
|
webnotes.msgprint("Please enter Warehouse for which Purchase Request will be raised",
|
||||||
|
raise_exception=1)
|
||||||
|
|
||||||
|
bom_dict = self.get_distinct_items_and_boms()[0]
|
||||||
|
self.get_raw_materials(bom_dict)
|
||||||
|
item_projected_qty = self.get_projected_qty()
|
||||||
|
|
||||||
|
from accounts.utils import get_fiscal_year
|
||||||
|
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||||
|
|
||||||
|
items_to_be_requested = webnotes._dict()
|
||||||
|
for item in self.item_dict:
|
||||||
|
if flt(self.item_dict[item][0]) > item_projected_qty[item]:
|
||||||
|
# shortage
|
||||||
|
requested_qty = flt(self.item_dict[item][0]) - item_projected_qty[item]
|
||||||
|
# comsider minimum order qty
|
||||||
|
requested_qty = requested_qty > flt(self.item_dict[item][3]) and \
|
||||||
|
requested_qty or flt(self.item_dict[item][3])
|
||||||
|
items_to_be_requested[item] = requested_qty
|
||||||
|
|
||||||
|
self.insert_purchase_request(items_to_be_requested, fiscal_year)
|
||||||
|
|
||||||
|
def get_projected_qty(self):
|
||||||
|
items = self.item_dict.keys()
|
||||||
|
item_projected_qty = webnotes.conn.sql("""select item_code, sum(projected_qty)
|
||||||
|
from `tabBin` where item_code in (%s) group by item_code""" %
|
||||||
|
(", ".join(["%s"]*len(items)),), tuple(items))
|
||||||
|
|
||||||
|
return dict(item_projected_qty)
|
||||||
|
|
||||||
|
def insert_purchase_request(self, items_to_be_requested, fiscal_year):
|
||||||
|
purchase_request_list = []
|
||||||
|
if items_to_be_requested:
|
||||||
|
for item in items_to_be_requested:
|
||||||
|
item_wrapper = webnotes.model_wrapper("Item", item)
|
||||||
|
pr_doclist = [
|
||||||
|
{
|
||||||
|
"doctype": "Purchase Request",
|
||||||
|
"__islocal": 1,
|
||||||
|
"naming_series": "IDT",
|
||||||
|
"transaction_date": nowdate(),
|
||||||
|
"status": "Draft",
|
||||||
|
"company": self.doc.company,
|
||||||
|
"fiscal_year": fiscal_year,
|
||||||
|
"requested_by": webnotes.session.user,
|
||||||
|
"remark": "Automatically raised from Production Planning Tool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Purchase Request Item",
|
||||||
|
"__islocal": 1,
|
||||||
|
"parentfield": "indent_details",
|
||||||
|
"item_code": item,
|
||||||
|
"item_name": item_wrapper.doc.item_name,
|
||||||
|
"description": item_wrapper.doc.description,
|
||||||
|
"uom": item_wrapper.doc.stock_uom,
|
||||||
|
"item_group": item_wrapper.doc.item_group,
|
||||||
|
"brand": item_wrapper.doc.brand,
|
||||||
|
"qty": items_to_be_requested[item],
|
||||||
|
"schedule_date": add_days(nowdate(), cint(item_wrapper.doc.lead_time_days)),
|
||||||
|
"warehouse": self.doc.purchase_request_for_warehouse
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pr_wrapper = webnotes.model_wrapper(pr_doclist)
|
||||||
|
pr_wrapper.ignore_permissions = 1
|
||||||
|
pr_wrapper.submit()
|
||||||
|
purchase_request_list.append(pr_wrapper.doc.name)
|
||||||
|
|
||||||
|
if purchase_request_list:
|
||||||
|
pur_req = ["""<a href="#Form/Purchase Request/%s" target="_blank">%s</a>""" % \
|
||||||
|
(p, p) for p in purchase_request_list]
|
||||||
|
webnotes.msgprint("Following Purchase Request created successfully: \n%s" %
|
||||||
|
"\n".join(pur_req))
|
||||||
|
else:
|
||||||
|
webnotes.msgprint("Nothing to request")
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
{
|
{
|
||||||
"owner": "jai@webnotestech.com",
|
"owner": "jai@webnotestech.com",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"creation": "2012-12-14 10:15:16",
|
"creation": "2013-01-16 14:48:56",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"modified": "2012-12-14 11:37:40"
|
"modified": "2013-01-17 11:39:55"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@@ -28,8 +28,10 @@
|
|||||||
"parent": "Production Planning Tool",
|
"parent": "Production Planning Tool",
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
|
"submit": 0,
|
||||||
"doctype": "DocPerm",
|
"doctype": "DocPerm",
|
||||||
"write": 1,
|
"write": 1,
|
||||||
|
"report": 0,
|
||||||
"parenttype": "DocType",
|
"parenttype": "DocType",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"parentfield": "permissions"
|
"parentfield": "permissions"
|
||||||
@@ -68,9 +70,9 @@
|
|||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
|
"reqd": 1,
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"reqd": 1,
|
|
||||||
"options": "Company"
|
"options": "Company"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -154,10 +156,19 @@
|
|||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"options": "Simple"
|
"options": "Simple"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
|
||||||
|
"default": "1",
|
||||||
|
"doctype": "DocField",
|
||||||
|
"label": "Use Multi-Level BOM",
|
||||||
|
"reqd": 0,
|
||||||
|
"fieldname": "use_multi_level_bom",
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"width": "50%",
|
"width": "50%",
|
||||||
"fieldname": "column_break5",
|
"fieldname": "cb5",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -170,18 +181,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"width": "50%",
|
"fieldname": "sb5",
|
||||||
"fieldname": "column_break6",
|
"fieldtype": "Section Break",
|
||||||
"fieldtype": "Column Break"
|
"options": "Simple"
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
|
|
||||||
"default": "1",
|
|
||||||
"doctype": "DocField",
|
|
||||||
"label": "Use Multi-Level BOM",
|
|
||||||
"fieldname": "use_multi_level_bom",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"reqd": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Download a report containing all raw materials with their latest inventory status",
|
"description": "Download a report containing all raw materials with their latest inventory status",
|
||||||
@@ -191,8 +193,25 @@
|
|||||||
"fieldtype": "Button"
|
"fieldtype": "Button"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "System Manager",
|
"doctype": "DocField",
|
||||||
"doctype": "DocPerm"
|
"width": "50%",
|
||||||
|
"fieldname": "column_break6",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"label": "Purchase Request For Warehouse",
|
||||||
|
"fieldname": "purchase_request_for_warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Warehouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Items to be requested which are \"Out of Stock\" considering all warehouses based on projected qty and minimum order qty",
|
||||||
|
"doctype": "DocField",
|
||||||
|
"label": "Raise Purchase Request",
|
||||||
|
"fieldname": "raise_purchase_request",
|
||||||
|
"fieldtype": "Button",
|
||||||
|
"options": "raise_purchase_request"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "Manufacturing User",
|
"role": "Manufacturing User",
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class DocType:
|
|||||||
|
|
||||||
def update_website(self):
|
def update_website(self):
|
||||||
from website.utils import update_page_name
|
from website.utils import update_page_name
|
||||||
update_page_name(self.doc, self.doc.item_name)
|
update_page_name(self.doc, self.doc.name + " " + self.doc.item_name)
|
||||||
|
|
||||||
from website.helpers.product import invalidate_cache_for
|
from website.helpers.product import invalidate_cache_for
|
||||||
invalidate_cache_for(self.doc.item_group)
|
invalidate_cache_for(self.doc.item_group)
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ class DocType:
|
|||||||
from `tabDelivery Note Item` dni
|
from `tabDelivery Note Item` dni
|
||||||
where parent=%s and item_code in (%s)
|
where parent=%s and item_code in (%s)
|
||||||
group by item_code""" % ("%s", ", ".join(["%s"]*len(items))),
|
group by item_code""" % ("%s", ", ".join(["%s"]*len(items))),
|
||||||
tuple([self.doc.delivery_note] + items), as_dict=1, debug=1)
|
tuple([self.doc.delivery_note] + items), as_dict=1)
|
||||||
|
|
||||||
ps_item_qty = dict([[d.item_code, d.qty] for d in self.doclist])
|
ps_item_qty = dict([[d.item_code, d.qty] for d in self.doclist])
|
||||||
|
|
||||||
@@ -170,9 +170,6 @@ class DocType:
|
|||||||
webnotes.msgprint("Invalid new packed quantity for item %s. \
|
webnotes.msgprint("Invalid new packed quantity for item %s. \
|
||||||
Please try again or contact support@erpnext.com" % item['item_code'], raise_exception=1)
|
Please try again or contact support@erpnext.com" % item['item_code'], raise_exception=1)
|
||||||
|
|
||||||
delivery_note_item = webnotes.conn.get_value("Delivery Note Item", {
|
|
||||||
"parent": self.doc.delivery_note, "item_code": item["item_code"]})
|
|
||||||
|
|
||||||
webnotes.conn.sql("""\
|
webnotes.conn.sql("""\
|
||||||
UPDATE `tabDelivery Note Item`
|
UPDATE `tabDelivery Note Item`
|
||||||
SET packed_qty = %s
|
SET packed_qty = %s
|
||||||
|
|||||||
@@ -226,7 +226,8 @@ cur_frm.cscript.s_warehouse = function(doc, cdt, cdn) {
|
|||||||
'warehouse' : cstr(d.s_warehouse) || cstr(d.t_warehouse),
|
'warehouse' : cstr(d.s_warehouse) || cstr(d.t_warehouse),
|
||||||
'transfer_qty' : d.transfer_qty,
|
'transfer_qty' : d.transfer_qty,
|
||||||
'serial_no' : d.serial_no,
|
'serial_no' : d.serial_no,
|
||||||
'bom_no' : d.bom_no
|
'bom_no' : d.bom_no,
|
||||||
|
'qty' : d.s_warehouse ? -1* d.qty : d.qty
|
||||||
}
|
}
|
||||||
get_server_fields('get_warehouse_details', JSON.stringify(args),
|
get_server_fields('get_warehouse_details', JSON.stringify(args),
|
||||||
'mtn_details', doc, cdt, cdn, 1);
|
'mtn_details', doc, cdt, cdn, 1);
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ from webnotes.model.code import get_obj
|
|||||||
from webnotes import msgprint, _
|
from webnotes import msgprint, _
|
||||||
from stock.utils import get_incoming_rate
|
from stock.utils import get_incoming_rate
|
||||||
from stock.stock_ledger import get_previous_sle
|
from stock.stock_ledger import get_previous_sle
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
sql = webnotes.conn.sql
|
sql = webnotes.conn.sql
|
||||||
|
|
||||||
@@ -157,24 +159,47 @@ class DocType(TransactionBase):
|
|||||||
def get_stock_and_rate(self):
|
def get_stock_and_rate(self):
|
||||||
"""get stock and incoming rate on posting date"""
|
"""get stock and incoming rate on posting date"""
|
||||||
for d in getlist(self.doclist, 'mtn_details'):
|
for d in getlist(self.doclist, 'mtn_details'):
|
||||||
args = {
|
args = webnotes._dict({
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"warehouse": d.s_warehouse or d.t_warehouse,
|
"warehouse": d.s_warehouse or d.t_warehouse,
|
||||||
"posting_date": self.doc.posting_date,
|
"posting_date": self.doc.posting_date,
|
||||||
"posting_time": self.doc.posting_time,
|
"posting_time": self.doc.posting_time,
|
||||||
"qty": d.transfer_qty,
|
"qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty,
|
||||||
"serial_no": d.serial_no,
|
"serial_no": d.serial_no,
|
||||||
"bom_no": d.bom_no
|
"bom_no": d.bom_no,
|
||||||
}
|
})
|
||||||
# get actual stock at source warehouse
|
# get actual stock at source warehouse
|
||||||
d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0
|
d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0
|
||||||
|
|
||||||
# get incoming rate
|
# get incoming rate
|
||||||
if not flt(d.incoming_rate):
|
if not flt(d.incoming_rate) or self.doc.purpose == "Sales Return":
|
||||||
d.incoming_rate = get_incoming_rate(args)
|
d.incoming_rate = self.get_incoming_rate(args)
|
||||||
|
|
||||||
d.amount = flt(d.qty) * flt(d.incoming_rate)
|
d.amount = flt(d.qty) * flt(d.incoming_rate)
|
||||||
|
|
||||||
|
def get_incoming_rate(self, args):
|
||||||
|
if self.doc.purpose == "Sales Return" and \
|
||||||
|
(self.doc.delivery_note_no or self.doc.sales_invoice_no):
|
||||||
|
sle = webnotes.conn.sql("""select name, posting_date, posting_time,
|
||||||
|
actual_qty, stock_value from `tabStock Ledger Entry`
|
||||||
|
where voucher_type = %s and voucher_no = %s and
|
||||||
|
item_code = %s and ifnull(is_cancelled, 'No') = 'No' limit 1""",
|
||||||
|
((self.doc.delivery_note_no and "Delivery Note" or "Sales Invoice"),
|
||||||
|
self.doc.delivery_note_no or self.doc.sales_invoice_no, args.item_code), as_dict=1)
|
||||||
|
if sle:
|
||||||
|
args.update({
|
||||||
|
"posting_date": sle[0].posting_date,
|
||||||
|
"posting_time": sle[0].posting_time,
|
||||||
|
"sle": sle[0].name
|
||||||
|
})
|
||||||
|
previous_sle = get_previous_sle(args)
|
||||||
|
incoming_rate = (flt(sle[0].stock_value) - flt(previous_sle.get("stock_value"))) / \
|
||||||
|
flt(sle[0].actual_qty)
|
||||||
|
else:
|
||||||
|
incoming_rate = get_incoming_rate(args)
|
||||||
|
|
||||||
|
return incoming_rate
|
||||||
|
|
||||||
def validate_incoming_rate(self):
|
def validate_incoming_rate(self):
|
||||||
for d in getlist(self.doclist, 'mtn_details'):
|
for d in getlist(self.doclist, 'mtn_details'):
|
||||||
if not flt(d.incoming_rate) and d.t_warehouse:
|
if not flt(d.incoming_rate) and d.t_warehouse:
|
||||||
@@ -264,8 +289,7 @@ class DocType(TransactionBase):
|
|||||||
pro_obj.doc.save()
|
pro_obj.doc.save()
|
||||||
|
|
||||||
def get_item_details(self, arg):
|
def get_item_details(self, arg):
|
||||||
import json
|
arg = json.loads(arg)
|
||||||
arg, actual_qty, in_rate = json.loads(arg), 0, 0
|
|
||||||
|
|
||||||
item = sql("""select stock_uom, description, item_name from `tabItem`
|
item = sql("""select stock_uom, description, item_name from `tabItem`
|
||||||
where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00'
|
where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00'
|
||||||
@@ -305,16 +329,16 @@ class DocType(TransactionBase):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_warehouse_details(self, args):
|
def get_warehouse_details(self, args):
|
||||||
import json
|
args = json.loads(args)
|
||||||
args, actual_qty, in_rate = json.loads(args), 0, 0
|
|
||||||
args.update({
|
args.update({
|
||||||
"posting_date": self.doc.posting_date,
|
"posting_date": self.doc.posting_date,
|
||||||
"posting_time": self.doc.posting_time
|
"posting_time": self.doc.posting_time,
|
||||||
})
|
})
|
||||||
|
args = webnotes._dict(args)
|
||||||
|
|
||||||
ret = {
|
ret = {
|
||||||
"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
|
"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
|
||||||
"incoming_rate" : get_incoming_rate(args)
|
"incoming_rate" : self.get_incoming_rate(args)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
{
|
{
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"creation": "2013-01-14 15:14:40",
|
"creation": "2013-01-15 12:28:57",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"modified": "2013-01-15 12:25:13"
|
"modified": "2013-01-16 13:59:28"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_attach": 0,
|
"allow_attach": 0,
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"search_fields": "posting_date",
|
"search_fields": "posting_date",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
|
"read_only_onload": 0,
|
||||||
"autoname": "SR/.######",
|
"autoname": "SR/.######",
|
||||||
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
|
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
|
||||||
"allow_email": 1,
|
"allow_email": 1,
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
"read": 1,
|
"read": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"name": "__common__",
|
"name": "__common__",
|
||||||
"amend": 0,
|
"amend": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"doctype": "DocPerm",
|
"doctype": "DocPerm",
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
|
|||||||
@@ -56,14 +56,11 @@ def update_entries_after(args, verbose=1):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if sle.serial_no:
|
if sle.serial_no:
|
||||||
valuation_rate = get_serialized_values(qty_after_transaction, sle,
|
valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
|
||||||
valuation_rate)
|
|
||||||
elif valuation_method == "Moving Average":
|
elif valuation_method == "Moving Average":
|
||||||
valuation_rate = get_moving_average_values(qty_after_transaction, sle,
|
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate)
|
||||||
valuation_rate)
|
|
||||||
else:
|
else:
|
||||||
valuation_rate = get_fifo_values(qty_after_transaction, sle,
|
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue)
|
||||||
stock_queue)
|
|
||||||
|
|
||||||
qty_after_transaction += flt(sle.actual_qty)
|
qty_after_transaction += flt(sle.actual_qty)
|
||||||
|
|
||||||
|
|||||||
@@ -78,12 +78,11 @@ def get_incoming_rate(args):
|
|||||||
valuation_method = get_valuation_method(args.get("item_code"))
|
valuation_method = get_valuation_method(args.get("item_code"))
|
||||||
previous_sle = get_previous_sle(args)
|
previous_sle = get_previous_sle(args)
|
||||||
if valuation_method == 'FIFO':
|
if valuation_method == 'FIFO':
|
||||||
# get rate based on the last item value?
|
if not previous_sle:
|
||||||
if args.get("qty"):
|
return 0.0
|
||||||
if not previous_sle:
|
previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]'))
|
||||||
return 0.0
|
in_rate = previous_stock_queue and \
|
||||||
stock_queue = json.loads(previous_sle.get('stock_queue', '[]'))
|
get_fifo_rate(previous_stock_queue, args.get("qty") or 0) or 0
|
||||||
in_rate = stock_queue and get_fifo_rate(stock_queue) or 0
|
|
||||||
elif valuation_method == 'Moving Average':
|
elif valuation_method == 'Moving Average':
|
||||||
in_rate = previous_sle.get('valuation_rate') or 0
|
in_rate = previous_sle.get('valuation_rate') or 0
|
||||||
return in_rate
|
return in_rate
|
||||||
@@ -104,13 +103,29 @@ def get_valuation_method(item_code):
|
|||||||
val_method = get_defaults().get('valuation_method', 'FIFO')
|
val_method = get_defaults().get('valuation_method', 'FIFO')
|
||||||
return val_method
|
return val_method
|
||||||
|
|
||||||
def get_fifo_rate(stock_queue):
|
def get_fifo_rate(previous_stock_queue, qty):
|
||||||
"""get FIFO (average) Rate from Stack"""
|
"""get FIFO (average) Rate from Queue"""
|
||||||
if not stock_queue:
|
if qty >= 0:
|
||||||
return 0.0
|
total = sum(f[0] for f in previous_stock_queue)
|
||||||
|
return total and sum(f[0] * f[1] for f in previous_stock_queue) / flt(total) or 0.0
|
||||||
total = sum(f[0] for f in stock_queue)
|
else:
|
||||||
return total and sum(f[0] * f[1] for f in stock_queue) / flt(total) or 0.0
|
outgoing_cost = 0
|
||||||
|
qty_to_pop = abs(qty)
|
||||||
|
while qty_to_pop and previous_stock_queue:
|
||||||
|
batch = previous_stock_queue[0]
|
||||||
|
if 0 < batch[0] <= qty_to_pop:
|
||||||
|
# if batch qty > 0
|
||||||
|
# not enough or exactly same qty in current batch, clear batch
|
||||||
|
outgoing_cost += flt(batch[0]) * flt(batch[1])
|
||||||
|
qty_to_pop -= batch[0]
|
||||||
|
previous_stock_queue.pop(0)
|
||||||
|
else:
|
||||||
|
# all from current batch
|
||||||
|
outgoing_cost += flt(qty_to_pop) * flt(batch[1])
|
||||||
|
batch[0] -= qty_to_pop
|
||||||
|
qty_to_pop = 0
|
||||||
|
# if queue gets blank and qty_to_pop remaining, get average rate of full queue
|
||||||
|
return outgoing_cost / abs(qty) - qty_to_pop
|
||||||
|
|
||||||
def get_valid_serial_nos(sr_nos, qty=0, item_code=''):
|
def get_valid_serial_nos(sr_nos, qty=0, item_code=''):
|
||||||
"""split serial nos, validate and return list of valid serial nos"""
|
"""split serial nos, validate and return list of valid serial nos"""
|
||||||
|
|||||||
@@ -13,15 +13,12 @@ wn.doclistviews['Maintenance Visit'] = wn.views.ListView.extend({
|
|||||||
|
|
||||||
]);
|
]);
|
||||||
this.stats = this.stats.concat(['completion_status', 'company']);
|
this.stats = this.stats.concat(['completion_status', 'company']);
|
||||||
//this.show_hide_check_column();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
prepare_data: function(data) {
|
prepare_data: function(data) {
|
||||||
this._super(data);
|
this._super(data);
|
||||||
data.mntc_date = wn.datetime.str_to_user(data.mntc_date);
|
data.mntc_date = wn.datetime.str_to_user(data.mntc_date);
|
||||||
data.mntc_time = wn.datetime.time_to_ampm(data.mntc_time);
|
data.date_time = "on " + data.mntc_date + " at " + data.mntc_time;
|
||||||
data.date_time = "on " + data.mntc_date + " at " +
|
|
||||||
data.mntc_time[0] + ":" + data.mntc_time[1] + " " + data.mntc_time[2];
|
|
||||||
data.customer_name = data.customer_name + " " + data.date_time;
|
data.customer_name = data.customer_name + " " + data.date_time;
|
||||||
data.completion_status = data.completion_status +
|
data.completion_status = data.completion_status +
|
||||||
(data.maintenance_type ? " [" + data.maintenance_type + "]": "");
|
(data.maintenance_type ? " [" + data.maintenance_type + "]": "");
|
||||||
|
|||||||
@@ -94,13 +94,11 @@ Calendar.prototype.show_event = function(ev, cal_ev) {
|
|||||||
d.onshow = function() {
|
d.onshow = function() {
|
||||||
// heading
|
// heading
|
||||||
var c = me.selected_date;
|
var c = me.selected_date;
|
||||||
var tmp = time_to_ampm(this.ev.event_hour);
|
|
||||||
tmp = tmp[0]+':'+tmp[1]+' '+tmp[2];
|
|
||||||
|
|
||||||
this.widgets['Heading'].innerHTML =
|
this.widgets['Heading'].innerHTML =
|
||||||
'<div style="text-align: center; padding:4px; font-size: 14px">'
|
'<div style="text-align: center; padding:4px; font-size: 14px">'
|
||||||
+ erpnext.calendar.weekdays[c.getDay()] + ', ' + c.getDate() + ' ' + month_list_full[c.getMonth()] + ' ' + c.getFullYear()
|
+ erpnext.calendar.weekdays[c.getDay()] + ', ' + c.getDate() + ' ' + month_list_full[c.getMonth()] + ' ' + c.getFullYear()
|
||||||
+ ' - <b>'+tmp+'</b></div>';
|
+ ' - <b>'+this.ev.event_hour+'</b></div>';
|
||||||
|
|
||||||
// set
|
// set
|
||||||
this.widgets['Description'].value = cstr(this.ev.description);
|
this.widgets['Description'].value = cstr(this.ev.description);
|
||||||
@@ -175,7 +173,7 @@ Calendar.prototype.add_event = function() {
|
|||||||
ev = locals['Event'][ev];
|
ev = locals['Event'][ev];
|
||||||
|
|
||||||
ev.event_date = dateutil.obj_to_str(this.selected_date);
|
ev.event_date = dateutil.obj_to_str(this.selected_date);
|
||||||
ev.event_hour = this.selected_hour+':00';
|
ev.event_hour = this.selected_hour+':00:00';
|
||||||
ev.event_type = 'Private';
|
ev.event_type = 'Private';
|
||||||
|
|
||||||
this.show_event(ev);
|
this.show_event(ev);
|
||||||
@@ -447,8 +445,7 @@ Calendar.DayView.prototype.create_table = function() {
|
|||||||
for(var j=0;j<2;j++) {
|
for(var j=0;j<2;j++) {
|
||||||
var cell = r.insertCell(j);
|
var cell = r.insertCell(j);
|
||||||
if(j==0) {
|
if(j==0) {
|
||||||
var tmp = time_to_ampm((i)+':00');
|
cell.innerHTML = i+':00:00';
|
||||||
cell.innerHTML = tmp[0]+':'+tmp[1]+' '+tmp[2];
|
|
||||||
$w(cell, '10%');
|
$w(cell, '10%');
|
||||||
} else {
|
} else {
|
||||||
cell.viewunit = new Calendar.DayViewUnit(cell);
|
cell.viewunit = new Calendar.DayViewUnit(cell);
|
||||||
@@ -510,9 +507,7 @@ Calendar.WeekView.prototype.create_table = function() {
|
|||||||
for(var j=0;j<8;j++) {
|
for(var j=0;j<8;j++) {
|
||||||
var cell = r.insertCell(j);
|
var cell = r.insertCell(j);
|
||||||
if(j==0) {
|
if(j==0) {
|
||||||
var tmp = time_to_ampm(i+':00');
|
cell.innerHTML = i+':00:00';
|
||||||
cell.innerHTML = tmp[0]+':'+tmp[1]+' '+tmp[2];
|
|
||||||
|
|
||||||
$w(cell, '10%');
|
$w(cell, '10%');
|
||||||
} else {
|
} else {
|
||||||
cell.viewunit = new Calendar.WeekViewUnit(cell);
|
cell.viewunit = new Calendar.WeekViewUnit(cell);
|
||||||
|
|||||||
@@ -97,7 +97,14 @@ def page_name(title):
|
|||||||
import re
|
import re
|
||||||
name = title.lower()
|
name = title.lower()
|
||||||
name = re.sub('[~!@#$%^&*()<>,."\']', '', name)
|
name = re.sub('[~!@#$%^&*()<>,."\']', '', name)
|
||||||
return '-'.join(name.split()[:8])
|
name = re.sub('[:/]', '-', name)
|
||||||
|
|
||||||
|
name = '-'.join(name.split())
|
||||||
|
|
||||||
|
# replace repeating hyphens
|
||||||
|
name = re.sub(r"(-)\1+", r"\1", name)
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
def update_page_name(doc, title):
|
def update_page_name(doc, title):
|
||||||
"""set page_name and check if it is unique"""
|
"""set page_name and check if it is unique"""
|
||||||
|
|||||||
Reference in New Issue
Block a user