Merge branch 'master' of github.com:webnotes/erpnext

This commit is contained in:
Priya
2013-10-10 12:43:22 +05:30
29 changed files with 359 additions and 402 deletions

View File

@@ -335,7 +335,7 @@ class DocType(BuyingController):
) )
# tax table gl entries # tax table gl entries
valuation_tax = 0 valuation_tax = {}
for tax in self.doclist.get({"parentfield": "purchase_tax_details"}): for tax in self.doclist.get({"parentfield": "purchase_tax_details"}):
if tax.category in ("Total", "Valuation and Total") and flt(tax.tax_amount): if tax.category in ("Total", "Valuation and Total") and flt(tax.tax_amount):
gl_entries.append( gl_entries.append(
@@ -350,8 +350,11 @@ class DocType(BuyingController):
) )
# accumulate valuation tax # accumulate valuation tax
if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount): if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount) \
valuation_tax += (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) and tax.cost_center:
valuation_tax.setdefault(tax.cost_center, 0)
valuation_tax[tax.cost_center] += \
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
# item gl entries # item gl entries
stock_item_and_auto_accounting_for_stock = False stock_item_and_auto_accounting_for_stock = False
@@ -392,15 +395,19 @@ class DocType(BuyingController):
if stock_item_and_auto_accounting_for_stock and valuation_tax: if stock_item_and_auto_accounting_for_stock and valuation_tax:
# credit valuation tax amount in "Expenses Included In Valuation" # credit valuation tax amount in "Expenses Included In Valuation"
# this will balance out valuation amount included in cost of goods sold # this will balance out valuation amount included in cost of goods sold
gl_entries.append( expenses_included_in_valuation = \
self.get_gl_dict({ self.get_company_default("expenses_included_in_valuation")
"account": self.get_company_default("expenses_included_in_valuation"),
"cost_center": self.get_company_default("cost_center"), for cost_center, amount in valuation_tax.items():
"against": self.doc.credit_to, gl_entries.append(
"credit": valuation_tax, self.get_gl_dict({
"remarks": self.doc.remarks or "Accounting Entry for Stock" "account": expenses_included_in_valuation,
}) "cost_center": cost_center,
) "against": self.doc.credit_to,
"credit": amount,
"remarks": self.doc.remarks or "Accounting Entry for Stock"
})
)
# writeoff account includes petty difference in the invoice amount # writeoff account includes petty difference in the invoice amount
# and the amount that is paid # and the amount that is paid

View File

@@ -83,7 +83,6 @@ class DocType(SellingController):
def on_submit(self): def on_submit(self):
if cint(self.doc.update_stock) == 1: if cint(self.doc.update_stock) == 1:
self.update_stock_ledger() self.update_stock_ledger()
self.update_serial_nos()
else: else:
# Check for Approving Authority # Check for Approving Authority
if not self.doc.recurring_id: if not self.doc.recurring_id:
@@ -111,7 +110,6 @@ class DocType(SellingController):
def on_cancel(self): def on_cancel(self):
if cint(self.doc.update_stock) == 1: if cint(self.doc.update_stock) == 1:
self.update_stock_ledger() self.update_stock_ledger()
self.update_serial_nos(cancel = True)
sales_com_obj = get_obj(dt = 'Sales Common') sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self) sales_com_obj.check_stop_sales_order(self)

View File

@@ -81,12 +81,12 @@ def get_tax_accounts(item_list, columns):
if account_head not in tax_accounts: if account_head not in tax_accounts:
tax_accounts.append(account_head) tax_accounts.append(account_head)
invoice = item_tax.setdefault(parent, {})
if item_wise_tax_detail: if item_wise_tax_detail:
try: try:
item_wise_tax_detail = json.loads(item_wise_tax_detail) item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item, tax_amount in item_wise_tax_detail.items(): for item, tax_amount in item_wise_tax_detail.items():
invoice.setdefault(item, {})[account_head] = flt(tax_amount) item_tax.setdefault(parent, {}).setdefault(item, {})[account_head] = \
flt(tax_amount[1])
except ValueError: except ValueError:
continue continue

View File

@@ -71,19 +71,19 @@ def get_tax_accounts(item_list, columns):
tax_details = webnotes.conn.sql("""select parent, account_head, item_wise_tax_detail tax_details = webnotes.conn.sql("""select parent, account_head, item_wise_tax_detail
from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
and docstatus = 1 and ifnull(account_head, '') != '' and docstatus = 1 and ifnull(account_head, '') != ''
and parent in (%s)""" % ', '.join(['%s']*len(item_list)), tuple([item.parent for item in item_list])) and parent in (%s)""" % ', '.join(['%s']*len(item_list)),
tuple([item.parent for item in item_list]))
for parent, account_head, item_wise_tax_detail in tax_details: for parent, account_head, item_wise_tax_detail in tax_details:
if account_head not in tax_accounts: if account_head not in tax_accounts:
tax_accounts.append(account_head) tax_accounts.append(account_head)
invoice = item_tax.setdefault(parent, {})
if item_wise_tax_detail: if item_wise_tax_detail:
try: try:
item_wise_tax_detail = json.loads(item_wise_tax_detail) item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item, tax_amount in item_wise_tax_detail.items(): for item, tax_amount in item_wise_tax_detail.items():
invoice.setdefault(item, {})[account_head] = flt(tax_amount) item_tax.setdefault(parent, {}).setdefault(item, {})[account_head] = \
flt(tax_amount[1])
except ValueError: except ValueError:
continue continue

View File

@@ -145,6 +145,11 @@ wn.module_page["Buying"] = [
route: "query-report/Purchase Order Trends", route: "query-report/Purchase Order Trends",
doctype: "Purchase Order" doctype: "Purchase Order"
}, },
{
"label":wn._("Supplier Addresses And Contacts"),
route: "query-report/Supplier Addresses and Contacts",
doctype: "Supplier"
},
] ]
} }
] ]

View File

@@ -0,0 +1,22 @@
[
{
"creation": "2013-10-09 10:38:40",
"docstatus": 0,
"modified": "2013-10-09 10:53:52",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"query": "SELECT\n `tabSupplier`.name as \"Supplier:Link/Supplier:120\",\n\t`tabSupplier`.supplier_name as \"Supplier Name::120\",\n\t`tabSupplier`.supplier_type as \"Supplier Type:Link/Supplier Type:120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2), \n\t\ttabAddress.state, tabAddress.pincode, tabAddress.country\n\t) as 'Address::180',\n concat_ws(', ', `tabContact`.first_name, `tabContact`.last_name) as 'Contact Name::180',\n\t`tabContact`.phone as \"Phone\",\n\t`tabContact`.mobile_no as \"Mobile No\",\n\t`tabContact`.email_id as \"Email Id::120\",\n\t`tabContact`.is_primary_contact as \"Is Primary Contact::120\"\nFROM\n\t`tabSupplier`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.supplier=`tabSupplier`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.supplier=`tabSupplier`.name\n\t)\nWHERE\n\t`tabSupplier`.docstatus<2\nORDER BY\n\t`tabSupplier`.name asc",
"ref_doctype": "Supplier",
"report_name": "Supplier Addresses and Contacts",
"report_type": "Query Report"
},
{
"doctype": "Report",
"name": "Supplier Addresses and Contacts"
}
]

View File

@@ -24,7 +24,7 @@ class BuyingController(StockController):
self.doc.supplier_name = webnotes.conn.get_value("Supplier", self.doc.supplier_name = webnotes.conn.get_value("Supplier",
self.doc.supplier, "supplier_name") self.doc.supplier, "supplier_name")
self.validate_stock_or_nonstock_items() self.validate_stock_or_nonstock_items()
self.validate_warehouse_belongs_to_company() self.validate_warehouse()
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate) super(BuyingController, self).set_missing_values(for_validate)
@@ -50,17 +50,20 @@ class BuyingController(StockController):
self.doc.supplier = supplier self.doc.supplier = supplier
break break
def validate_warehouse(self):
from stock.utils import validate_warehouse_user, validate_warehouse_company
warehouses = list(set([d.warehouse for d in
self.doclist.get({"doctype": self.tname}) if d.warehouse]))
for w in warehouses:
validate_warehouse_user(w)
validate_warehouse_company(w, self.doc.company)
def get_purchase_tax_details(self): def get_purchase_tax_details(self):
self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details") self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details")
self.set_taxes("purchase_tax_details", "purchase_other_charges") self.set_taxes("purchase_tax_details", "purchase_other_charges")
def validate_warehouse_belongs_to_company(self):
for warehouse, company in webnotes.conn.get_values("Warehouse",
self.doclist.get_distinct_values("warehouse"), "company").items():
if company and company != self.doc.company:
webnotes.msgprint(_("Company mismatch for Warehouse") + (": %s" % (warehouse,)),
raise_exception=WrongWarehouseCompany)
def validate_stock_or_nonstock_items(self): def validate_stock_or_nonstock_items(self):
if not self.get_stock_items(): if not self.get_stock_items():
tax_for_valuation = [d.account_head for d in tax_for_valuation = [d.account_head for d in

View File

@@ -233,34 +233,4 @@ class SellingController(StockController):
self.doc.order_type = "Sales" self.doc.order_type = "Sales"
elif self.doc.order_type not in valid_types: elif self.doc.order_type not in valid_types:
msgprint(_(self.meta.get_label("order_type")) + " " + msgprint(_(self.meta.get_label("order_type")) + " " +
_("must be one of") + ": " + comma_or(valid_types), _("must be one of") + ": " + comma_or(valid_types), raise_exception=True)
raise_exception=True)
def update_serial_nos(self, cancel=False):
from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
update_serial_nos_after_submit(self, self.doc.doctype, self.fname)
update_serial_nos_after_submit(self, self.doc.doctype, "packing_details")
for table_fieldname in (self.fname, "packing_details"):
for d in self.doclist.get({"parentfield": table_fieldname}):
for serial_no in get_serial_nos(d.serial_no):
sr = webnotes.bean("Serial No", serial_no)
if cancel:
sr.doc.status = "Available"
for fieldname in ("warranty_expiry_date", "delivery_document_type",
"delivery_document_no", "delivery_date", "delivery_time", "customer",
"customer_name"):
sr.doc.fields[fieldname] = None
else:
sr.doc.delivery_document_type = self.doc.doctype
sr.doc.delivery_document_no = self.doc.name
sr.doc.delivery_date = self.doc.posting_date
sr.doc.delivery_time = self.doc.posting_time
sr.doc.customer = self.doc.customer
sr.doc.customer_name = self.doc.customer_name
if sr.doc.warranty_period:
sr.doc.warranty_expiry_date = add_days(cstr(self.doc.posting_date),
cint(sr.doc.warranty_period))
sr.doc.status = 'Delivered'
sr.save()

View File

@@ -4,7 +4,6 @@
wn.provide("erpnext.hr"); wn.provide("erpnext.hr");
erpnext.hr.EmployeeController = wn.ui.form.Controller.extend({ erpnext.hr.EmployeeController = wn.ui.form.Controller.extend({
setup: function() { setup: function() {
this.setup_leave_approver_select();
this.frm.fields_dict.user_id.get_query = function(doc,cdt,cdn) { this.frm.fields_dict.user_id.get_query = function(doc,cdt,cdn) {
return { query:"core.doctype.profile.profile.profile_query"} } return { query:"core.doctype.profile.profile.profile_query"} }
this.frm.fields_dict.reports_to.get_query = function(doc,cdt,cdn) { this.frm.fields_dict.reports_to.get_query = function(doc,cdt,cdn) {
@@ -12,6 +11,7 @@ erpnext.hr.EmployeeController = wn.ui.form.Controller.extend({
}, },
onload: function() { onload: function() {
this.setup_leave_approver_select();
this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"], this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"],
wn.control_panel.country==="India"); wn.control_panel.country==="India");
if(this.frm.doc.__islocal) this.frm.set_value("employee_name", ""); if(this.frm.doc.__islocal) this.frm.set_value("employee_name", "");

View File

@@ -22,13 +22,14 @@ class DocType:
utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped",
"In Process", "Completed", "Cancelled"]) "In Process", "Completed", "Cancelled"])
if self.doc.production_item : self.validate_bom_no()
item_detail = sql("select name from `tabItem` where name = '%s' and docstatus != 2" self.validate_sales_order()
% self.doc.production_item, as_dict = 1) self.validate_warehouse()
if not item_detail:
msgprint("Item '%s' does not exist or cancelled in the system."
% cstr(self.doc.production_item), raise_exception=1)
from utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self.doclist, "stock_uom", ["qty", "produced_qty"])
def validate_bom_no(self):
if self.doc.bom_no: if self.doc.bom_no:
bom = sql("""select name from `tabBOM` where name=%s and docstatus=1 bom = sql("""select name from `tabBOM` where name=%s and docstatus=1
and is_active=1 and item=%s""" and is_active=1 and item=%s"""
@@ -38,6 +39,7 @@ class DocType:
May be BOM not exists or inactive or not submitted May be BOM not exists or inactive or not submitted
or for some other item.""" % cstr(self.doc.bom_no), raise_exception=1) or for some other item.""" % cstr(self.doc.bom_no), raise_exception=1)
def validate_sales_order(self):
if self.doc.sales_order: if self.doc.sales_order:
if not webnotes.conn.sql("""select name from `tabSales Order` if not webnotes.conn.sql("""select name from `tabSales Order`
where name=%s and docstatus = 1""", self.doc.sales_order): where name=%s and docstatus = 1""", self.doc.sales_order):
@@ -45,9 +47,12 @@ class DocType:
self.validate_production_order_against_so() self.validate_production_order_against_so()
from utilities.transaction_base import validate_uom_is_integer def validate_warehouse(self):
validate_uom_is_integer(self.doclist, "stock_uom", ["qty", "produced_qty"]) from stock.utils import validate_warehouse_user, validate_warehouse_company
for w in [self.doc.fg_warehouse, self.doc.wip_warehouse]:
validate_warehouse_user(w)
validate_warehouse_company(w, self.doc.company)
def validate_production_order_against_so(self): def validate_production_order_against_so(self):
# already ordered qty # already ordered qty

View File

@@ -6,35 +6,13 @@ import webnotes
from webnotes.utils import flt from webnotes.utils import flt
def execute(): def execute():
serial_nos = webnotes.conn.sql("""select name, item_code, status from `tabSerial No` serial_nos = webnotes.conn.sql("""select name from `tabSerial No` where status!='Not in Use'
where status!='Not in Use'""", as_dict=1) and docstatus=0""")
for sr in serial_nos: for sr in serial_nos:
last_sle = webnotes.conn.sql("""select voucher_type, voucher_no, actual_qty sr_bean = webnotes.bean("Serial No", sr[0])
from `tabStock Ledger Entry` where serial_no like %s and item_code=%s sr_bean.make_controller().via_stock_ledger = True
order by name desc limit 1""", sr_bean.run_method("validate")
("%%%s%%" % sr.name, sr.item_code), as_dict=1) sr_bean.save()
if flt(last_sle[0].actual_qty) > 0:
if last_sle[0].voucher_type == "Stock Entry" and webnotes.conn.get_value("Stock Entry",
last_sle[0].voucher_no, "purpose") == "Sales Return":
status = "Sales Returned"
else:
status = "Available"
else:
if last_sle[0].voucher_type == "Stock Entry":
purpose = webnotes.conn.get_value("Stock Entry", last_sle[0].voucher_no, "purpose")
if purpose == "Purchase Return":
status = "Purchase Returned"
else:
status = "Not Available"
else:
status = "Delivered"
if sr.status != status:
webnotes.conn.sql("""update `tabSerial No` set status=%s where name=%s""",
(status, sr.name))
webnotes.conn.sql("""update `tabSerial No` set warehouse=''
where status in ('Delivered', 'Purchase Returned')""")
webnotes.conn.sql("""update `tabSerial No` set warehouse='' where status in
('Delivered', 'Purchase Returned')""")

View File

@@ -29,9 +29,10 @@ def execute():
cost_center = "Default CC Ledger - NISL" cost_center = "Default CC Ledger - NISL"
for bin in webnotes.conn.sql("""select * from tabBin bin where ifnull(item_code, '')!='' for bin in webnotes.conn.sql("""select * from tabBin bin where ifnull(item_code, '')!=''
and ifnull(warehouse, '')!='' and ifnull(actual_qty, 0) != 0 and ifnull(warehouse, '') in (%s) and ifnull(actual_qty, 0) != 0
and (select company from tabWarehouse where name=bin.warehouse)=%s""", and (select company from tabWarehouse where name=bin.warehouse)=%s""" %
company[0], as_dict=1): (', '.join(['%s']*len(warehouse_map)), '%s'),
(warehouse_map.keys() + [company[0]]), as_dict=1):
item_details = item_map[bin.item_code] item_details = item_map[bin.item_code]
new_warehouse = warehouse_map[bin.warehouse].get("fixed_asset_warehouse") \ new_warehouse = warehouse_map[bin.warehouse].get("fixed_asset_warehouse") \
if cstr(item_details.is_asset_item) == "Yes" \ if cstr(item_details.is_asset_item) == "Yes" \
@@ -40,7 +41,8 @@ def execute():
if item_details.has_serial_no == "Yes": if item_details.has_serial_no == "Yes":
serial_no = "\n".join([d[0] for d in webnotes.conn.sql("""select name serial_no = "\n".join([d[0] for d in webnotes.conn.sql("""select name
from `tabSerial No` where item_code = %s and warehouse = %s from `tabSerial No` where item_code = %s and warehouse = %s
and status='Available'""", (bin.item_code, bin.warehouse))]) and status in ('Available', 'Sales Returned')""",
(bin.item_code, bin.warehouse))])
else: else:
serial_no = None serial_no = None

View File

@@ -133,4 +133,4 @@ def make_opportunity(source_name, target_doclist=None):
} }
}}, target_doclist) }}, target_doclist)
return [d.fields for d in doclist] return [d if isinstance(d, dict) else d.fields for d in doclist]

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-04-10 11:45:37", "creation": "2013-04-10 11:45:37",
"docstatus": 0, "docstatus": 0,
"modified": "2013-09-26 16:30:36", "modified": "2013-10-09 15:27:54",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -174,19 +174,6 @@
"options": "Profile", "options": "Profile",
"search_index": 1 "search_index": 1
}, },
{
"depends_on": "eval:!doc.__islocal",
"description": "Date on which the lead was last contacted",
"doctype": "DocField",
"fieldname": "last_contact_date",
"fieldtype": "Date",
"label": "Last Contact Date",
"no_copy": 1,
"oldfieldname": "last_contact_date",
"oldfieldtype": "Date",
"print_hide": 1,
"read_only": 1
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "col_break123", "fieldname": "col_break123",

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import cstr, getdate, cint from webnotes.utils import cstr, cint
from webnotes.model.bean import getlist from webnotes.model.bean import getlist
from webnotes import msgprint from webnotes import msgprint
@@ -70,10 +70,6 @@ class DocType(TransactionBase):
return ret return ret
def on_update(self): def on_update(self):
# Add to calendar
if self.doc.contact_date and self.doc.contact_date_ref != self.doc.contact_date:
webnotes.conn.set(self.doc, 'contact_date_ref',self.doc.contact_date)
self.add_calendar_event() self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False): def add_calendar_event(self, opts=None, force=False):
@@ -101,14 +97,6 @@ class DocType(TransactionBase):
super(DocType, self).add_calendar_event(opts, force) super(DocType, self).add_calendar_event(opts, force)
def set_last_contact_date(self):
if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date:
if getdate(self.doc.contact_date_ref) < getdate(self.doc.contact_date):
self.doc.last_contact_date=self.doc.contact_date_ref
else:
msgprint("Contact Date Cannot be before Last Contact Date")
raise Exception
def validate_item_details(self): def validate_item_details(self):
if not getlist(self.doclist, 'enquiry_details'): if not getlist(self.doclist, 'enquiry_details'):
msgprint("Please select items for which enquiry needs to be made") msgprint("Please select items for which enquiry needs to be made")
@@ -121,7 +109,6 @@ class DocType(TransactionBase):
msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1) msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1)
def validate(self): def validate(self):
self.set_last_contact_date()
self.validate_item_details() self.validate_item_details()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust() self.validate_lead_cust()

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-03-07 18:50:30", "creation": "2013-03-07 18:50:30",
"docstatus": 0, "docstatus": 0,
"modified": "2013-09-25 19:32:29", "modified": "2013-10-09 15:26:29",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -408,20 +408,6 @@
"oldfieldtype": "Date", "oldfieldtype": "Date",
"read_only": 0 "read_only": 0
}, },
{
"allow_on_submit": 0,
"depends_on": "eval:!doc.__islocal",
"description": "Date on which the lead was last contacted",
"doctype": "DocField",
"fieldname": "last_contact_date",
"fieldtype": "Date",
"label": "Last Contact Date",
"no_copy": 1,
"oldfieldname": "last_contact_date",
"oldfieldtype": "Date",
"print_hide": 1,
"read_only": 1
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "to_discuss", "fieldname": "to_discuss",

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import cstr, getdate from webnotes.utils import cstr
from webnotes.model.bean import getlist from webnotes.model.bean import getlist
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
from webnotes import _, msgprint from webnotes import _, msgprint
@@ -93,19 +93,6 @@ class DocType(SellingController):
msgprint("You can not select non sales item "+d.item_code+" in Sales Quotation") msgprint("You can not select non sales item "+d.item_code+" in Sales Quotation")
raise Exception raise Exception
#--------------Validation For Last Contact Date-----------------
# ====================================================================================================================
def set_last_contact_date(self):
#if not self.doc.contact_date_ref:
#self.doc.contact_date_ref=self.doc.contact_date
#self.doc.last_contact_date=self.doc.contact_date_ref
if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date:
if getdate(self.doc.contact_date_ref) < getdate(self.doc.contact_date):
self.doc.last_contact_date=self.doc.contact_date_ref
else:
msgprint("Contact Date Cannot be before Last Contact Date")
raise Exception
def validate(self): def validate(self):
super(DocType, self).validate() super(DocType, self).validate()
@@ -116,7 +103,6 @@ class DocType(SellingController):
utilities.validate_status(self.doc.status, ["Draft", "Submitted", utilities.validate_status(self.doc.status, ["Draft", "Submitted",
"Order Confirmed", "Order Lost", "Cancelled"]) "Order Confirmed", "Order Lost", "Cancelled"])
self.set_last_contact_date()
self.validate_order_type() self.validate_order_type()
self.validate_for_items() self.validate_for_items()

View File

@@ -127,7 +127,7 @@ class DocType(SellingController):
self.validate_po() self.validate_po()
self.validate_uom_is_integer("stock_uom", "qty") self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items() self.validate_for_items()
self.validate_warehouse_user() self.validate_warehouse()
sales_com_obj = get_obj(dt = 'Sales Common') sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_active_sales_items(self) sales_com_obj.check_active_sales_items(self)
sales_com_obj.check_conversion_rate(self) sales_com_obj.check_conversion_rate(self)
@@ -148,14 +148,15 @@ class DocType(SellingController):
if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered' if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered'
def validate_warehouse_user(self): def validate_warehouse(self):
from stock.utils import validate_warehouse_user from stock.utils import validate_warehouse_user, validate_warehouse_company
warehouses = list(set([d.reserved_warehouse for d in warehouses = list(set([d.reserved_warehouse for d in
self.doclist.get({"doctype": self.tname}) if d.reserved_warehouse])) self.doclist.get({"doctype": self.tname}) if d.reserved_warehouse]))
for w in warehouses: for w in warehouses:
validate_warehouse_user(w) validate_warehouse_user(w)
validate_warehouse_company(w, self.doc.company)
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(DocType, self).validate_with_previous_doc(self.tname, { super(DocType, self).validate_with_previous_doc(self.tname, {

View File

@@ -185,7 +185,6 @@ class DocType(SellingController):
# create stock ledger entry # create stock ledger entry
self.update_stock_ledger() self.update_stock_ledger()
self.update_serial_nos()
self.credit_limit() self.credit_limit()
@@ -203,42 +202,12 @@ class DocType(SellingController):
self.update_prevdoc_status() self.update_prevdoc_status()
self.update_stock_ledger() self.update_stock_ledger()
self.update_serial_nos(cancel=True)
webnotes.conn.set(self.doc, 'status', 'Cancelled') webnotes.conn.set(self.doc, 'status', 'Cancelled')
self.cancel_packing_slips() self.cancel_packing_slips()
self.make_cancel_gl_entries() self.make_cancel_gl_entries()
def update_serial_nos(self, cancel=False):
from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
update_serial_nos_after_submit(self, "Delivery Note", "delivery_note_details")
update_serial_nos_after_submit(self, "Delivery Note", "packing_details")
for table_fieldname in ("delivery_note_details", "packing_details"):
for d in self.doclist.get({"parentfield": table_fieldname}):
for serial_no in get_serial_nos(d.serial_no):
sr = webnotes.bean("Serial No", serial_no)
if cancel:
sr.doc.status = "Available"
for fieldname in ("warranty_expiry_date", "delivery_document_type",
"delivery_document_no", "delivery_date", "delivery_time", "customer",
"customer_name"):
sr.doc.fields[fieldname] = None
else:
sr.doc.delivery_document_type = "Delivery Note"
sr.doc.delivery_document_no = self.doc.name
sr.doc.delivery_date = self.doc.posting_date
sr.doc.delivery_time = self.doc.posting_time
sr.doc.customer = self.doc.customer
sr.doc.customer_name = self.doc.customer_name
if sr.doc.warranty_period:
sr.doc.warranty_expiry_date = add_days(cstr(self.doc.posting_date),
cint(sr.doc.warranty_period))
sr.doc.status = 'Delivered'
sr.save()
def validate_packed_qty(self): def validate_packed_qty(self):
""" """
Validate that if packed qty exists, it should be equal to qty Validate that if packed qty exists, it should be equal to qty

View File

@@ -85,7 +85,6 @@ class DocType:
pr_bean = webnotes.bean("Purchase Receipt", pr) pr_bean = webnotes.bean("Purchase Receipt", pr)
pr_bean.run_method("update_ordered_qty", is_cancelled="Yes") pr_bean.run_method("update_ordered_qty", is_cancelled="Yes")
pr_bean.run_method("update_serial_nos", cancel=True)
webnotes.conn.sql("""delete from `tabStock Ledger Entry` webnotes.conn.sql("""delete from `tabStock Ledger Entry`
where voucher_type='Purchase Receipt' and voucher_no=%s""", pr) where voucher_type='Purchase Receipt' and voucher_no=%s""", pr)
@@ -97,5 +96,4 @@ class DocType:
pr_bean = webnotes.bean("Purchase Receipt", pr) pr_bean = webnotes.bean("Purchase Receipt", pr)
pr_bean.run_method("update_ordered_qty") pr_bean.run_method("update_ordered_qty")
pr_bean.run_method("update_stock") pr_bean.run_method("update_stock")
pr_bean.run_method("update_serial_nos")
pr_bean.run_method("make_gl_entries") pr_bean.run_method("make_gl_entries")

View File

@@ -68,23 +68,15 @@ class DocType(BuyingController):
self.doc.status = "Draft" self.doc.status = "Draft"
import utilities import utilities
utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", "Cancelled"])
"Cancelled"])
# restrict material request type
self.validate_value("material_request_type", "in", ["Purchase", "Transfer"]) self.validate_value("material_request_type", "in", ["Purchase", "Transfer"])
# Get Purchase Common Obj
pc_obj = get_obj(dt='Purchase Common') pc_obj = get_obj(dt='Purchase Common')
# Validate for items
pc_obj.validate_for_items(self) pc_obj.validate_for_items(self)
# Validate qty against SO
self.validate_qty_against_so() self.validate_qty_against_so()
def update_bin(self, is_submit, is_stopped): def update_bin(self, is_submit, is_stopped):
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'""" """ Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""

View File

@@ -246,27 +246,13 @@ class DocType(BuyingController):
self.update_stock() self.update_stock()
self.update_serial_nos() from stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
update_serial_nos_after_submit(self, "Purchase Receipt", "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 update_serial_nos(self, cancel=False):
from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
update_serial_nos_after_submit(self, "Purchase Receipt", "purchase_receipt_details")
for d in self.doclist.get({"parentfield": "purchase_receipt_details"}):
for serial_no in get_serial_nos(d.serial_no):
sr = webnotes.bean("Serial No", serial_no)
if cancel:
sr.doc.supplier = None
sr.doc.supplier_name = None
else:
sr.doc.supplier = self.doc.supplier
sr.doc.supplier_name = self.doc.supplier_name
sr.save()
def check_next_docstatus(self): def check_next_docstatus(self):
submit_rv = webnotes.conn.sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_receipt = '%s' and t1.docstatus = 1" % (self.doc.name)) submit_rv = webnotes.conn.sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_receipt = '%s' and t1.docstatus = 1" % (self.doc.name))
if submit_rv: if submit_rv:
@@ -292,7 +278,6 @@ class DocType(BuyingController):
self.update_ordered_qty() self.update_ordered_qty()
self.update_stock() self.update_stock()
self.update_serial_nos(cancel=True)
self.update_prevdoc_status() self.update_prevdoc_status()
pc_obj.update_last_purchase_rate(self, 0) pc_obj.update_last_purchase_rate(self, 0)

View File

@@ -4,14 +4,22 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import cint, getdate, nowdate from webnotes.utils import cint, getdate, nowdate, cstr, flt, add_days
import datetime import datetime
from webnotes import msgprint, _ from webnotes import msgprint, _, ValidationError
from controllers.stock_controller import StockController from controllers.stock_controller import StockController
class SerialNoCannotCreateDirectError(webnotes.ValidationError): pass class SerialNoCannotCreateDirectError(ValidationError): pass
class SerialNoCannotCannotChangeError(webnotes.ValidationError): pass class SerialNoCannotCannotChangeError(ValidationError): pass
class SerialNoNotRequiredError(ValidationError): pass
class SerialNoRequiredError(ValidationError): pass
class SerialNoQtyError(ValidationError): pass
class SerialNoItemError(ValidationError): pass
class SerialNoWarehouseError(ValidationError): pass
class SerialNoStatusError(ValidationError): pass
class SerialNoNotExistsError(ValidationError): pass
class SerialNoDuplicateError(ValidationError): pass
class DocType(StockController): class DocType(StockController):
def __init__(self, doc, doclist=[]): def __init__(self, doc, doclist=[]):
@@ -21,14 +29,19 @@ class DocType(StockController):
def validate(self): def validate(self):
if self.doc.fields.get("__islocal") and self.doc.warehouse: if self.doc.fields.get("__islocal") and self.doc.warehouse:
webnotes.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"), webnotes.throw(_("New Serial No cannot have Warehouse. Warehouse must be \
SerialNoCannotCreateDirectError) set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
self.validate_warranty_status() self.validate_warranty_status()
self.validate_amc_status() self.validate_amc_status()
self.validate_warehouse() self.validate_warehouse()
self.validate_item() self.validate_item()
if self.via_stock_ledger:
self.set_status()
self.set_purchase_details()
self.set_sales_details()
def validate_amc_status(self): def validate_amc_status(self):
""" """
validate amc status validate amc status
@@ -41,7 +54,8 @@ class DocType(StockController):
validate warranty status validate warranty status
""" """
if (self.doc.maintenance_status == 'Out of Warranty' and self.doc.warranty_expiry_date and getdate(self.doc.warranty_expiry_date) >= datetime.date.today()) or (self.doc.maintenance_status == 'Under Warranty' and (not self.doc.warranty_expiry_date or getdate(self.doc.warranty_expiry_date) < datetime.date.today())): if (self.doc.maintenance_status == 'Out of Warranty' and self.doc.warranty_expiry_date and getdate(self.doc.warranty_expiry_date) >= datetime.date.today()) or (self.doc.maintenance_status == 'Under Warranty' and (not self.doc.warranty_expiry_date or getdate(self.doc.warranty_expiry_date) < datetime.date.today())):
msgprint("Warranty expiry date and maintenance status mismatch. Please verify", raise_exception=1) msgprint("Warranty expiry date and maintenance status mismatch. Please verify",
raise_exception=1)
def validate_warehouse(self): def validate_warehouse(self):
@@ -49,10 +63,11 @@ class DocType(StockController):
item_code, warehouse = webnotes.conn.get_value("Serial No", item_code, warehouse = webnotes.conn.get_value("Serial No",
self.doc.name, ["item_code", "warehouse"]) self.doc.name, ["item_code", "warehouse"])
if item_code != self.doc.item_code: if item_code != self.doc.item_code:
webnotes.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError) webnotes.throw(_("Item Code cannot be changed for Serial No."),
SerialNoCannotCannotChangeError)
if not self.via_stock_ledger and warehouse != self.doc.warehouse: if not self.via_stock_ledger and warehouse != self.doc.warehouse:
webnotes.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError) webnotes.throw(_("Warehouse cannot be changed for Serial No."),
SerialNoCannotCannotChangeError)
def validate_item(self): def validate_item(self):
""" """
@@ -68,15 +83,88 @@ class DocType(StockController):
self.doc.brand = item.brand self.doc.brand = item.brand
self.doc.warranty_period = item.warranty_period self.doc.warranty_period = item.warranty_period
def set_status(self):
last_sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
where (serial_no like %s or serial_no like %s or serial_no=%s)
and item_code=%s and ifnull(is_cancelled, 'No')='No'
order by name desc limit 1""",
("%%%s%%" % (self.doc.name+"\n"), "%%%s%%" % ("\n"+self.doc.name), self.doc.name,
self.doc.item_code), as_dict=1)
if last_sle:
if last_sle[0].voucher_type == "Stock Entry":
document_type = webnotes.conn.get_value("Stock Entry", last_sle[0].voucher_no,
"purpose")
else:
document_type = last_sle[0].voucher_type
if last_sle[0].actual_qty > 0:
if document_type == "Sales Return":
self.doc.status = "Sales Returned"
else:
self.doc.status = "Available"
else:
if document_type == "Purchase Return":
self.doc.status = "Purchase Returned"
elif last_sle[0].voucher_type in ("Delivery Note", "Sales Invoice"):
self.doc.status = "Delivered"
else:
self.doc.status = "Not Available"
def set_purchase_details(self):
purchase_sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
where (serial_no like %s or serial_no like %s or serial_no=%s)
and item_code=%s and actual_qty > 0
and ifnull(is_cancelled, 'No')='No' order by name asc limit 1""",
("%%%s%%" % (self.doc.name+"\n"), "%%%s%%" % ("\n"+self.doc.name), self.doc.name,
self.doc.item_code), as_dict=1)
if purchase_sle:
self.doc.purchase_document_type = purchase_sle[0].voucher_type
self.doc.purchase_document_no = purchase_sle[0].voucher_no
self.doc.purchase_date = purchase_sle[0].posting_date
self.doc.purchase_time = purchase_sle[0].posting_time
self.doc.purchase_rate = purchase_sle[0].incoming_rate
if purchase_sle[0].voucher_type == "Purchase Receipt":
self.doc.supplier, self.doc.supplier_name = \
webnotes.conn.get_value("Purchase Receipt", purchase_sle[0].voucher_no,
["supplier", "supplier_name"])
else:
for fieldname in ("purchase_document_type", "purchase_document_no",
"purchase_date", "purchase_time", "purchase_rate", "supplier", "supplier_name"):
self.doc.fields[fieldname] = None
def set_sales_details(self):
delivery_sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
where (serial_no like %s or serial_no like %s or serial_no=%s)
and item_code=%s and actual_qty<0
and voucher_type in ('Delivery Note', 'Sales Invoice')
and ifnull(is_cancelled, 'No')='No' order by name desc limit 1""",
("%%%s%%" % (self.doc.name+"\n"), "%%%s%%" % ("\n"+self.doc.name), self.doc.name,
self.doc.item_code), as_dict=1)
if delivery_sle:
self.doc.delivery_document_type = delivery_sle[0].voucher_type
self.doc.delivery_document_no = delivery_sle[0].voucher_no
self.doc.delivery_date = delivery_sle[0].posting_date
self.doc.delivery_time = delivery_sle[0].posting_time
self.doc.customer, self.doc.customer_name = \
webnotes.conn.get_value(delivery_sle[0].voucher_type, delivery_sle[0].voucher_no,
["customer", "customer_name"])
if self.doc.warranty_period:
self.doc.warranty_expiry_date = add_days(cstr(delivery_sle[0].posting_date),
cint(self.doc.warranty_period))
else:
for fieldname in ("delivery_document_type", "delivery_document_no",
"delivery_date", "delivery_time", "customer", "customer_name",
"warranty_expiry_date"):
self.doc.fields[fieldname] = None
def on_trash(self): def on_trash(self):
if self.doc.status == 'Delivered': if self.doc.status == 'Delivered':
msgprint("Cannot trash Serial No : %s as it is already Delivered" % (self.doc.name), raise_exception = 1) webnotes.throw(_("Delivered Serial No ") + self.doc.name + _(" can not be deleted"))
if self.doc.warehouse: if self.doc.warehouse:
webnotes.throw(_("Cannot delete Serial No in warehouse. First remove from warehouse, then delete.") + \ webnotes.throw(_("Cannot delete Serial No in warehouse. \
": " + self.doc.name) First remove from warehouse, then delete.") + ": " + self.doc.name)
def on_cancel(self):
self.on_trash()
def on_rename(self, new, old, merge=False): def on_rename(self, new, old, merge=False):
"""rename serial_no text fields""" """rename serial_no text fields"""
@@ -93,3 +181,106 @@ class DocType(StockController):
webnotes.conn.sql("""update `tab%s` set serial_no = %s webnotes.conn.sql("""update `tab%s` set serial_no = %s
where name=%s""" % (dt[0], '%s', '%s'), where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(serial_nos), item[0])) ('\n'.join(serial_nos), item[0]))
def process_serial_no(sle):
item_det = get_item_details(sle.item_code)
validate_serial_no(sle, item_det)
update_serial_nos(sle, item_det)
def validate_serial_no(sle, item_det):
if item_det.has_serial_no=="No":
if sle.serial_no:
webnotes.throw(_("Serial Number should be blank for Non Serialized Item" + ": "
+ sle.item_code), SerialNoNotRequiredError)
else:
if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no)
if cint(sle.actual_qty) != flt(sle.actual_qty):
webnotes.throw(_("Serial No qty cannot be a fraction") + \
(": %s (%s)" % (sle.item_code, sle.actual_qty)))
if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
webnotes.throw(_("Serial Nos do not match with qty") + \
(": %s (%s)" % (sle.item_code, sle.actual_qty)), SerialNoQtyError)
for serial_no in serial_nos:
if webnotes.conn.exists("Serial No", serial_no):
sr = webnotes.bean("Serial No", serial_no)
if sr.doc.item_code!=sle.item_code:
webnotes.throw(_("Serial No does not belong to Item") +
(": %s (%s)" % (sle.item_code, serial_no)), SerialNoItemError)
if sr.doc.warehouse and sle.actual_qty > 0:
webnotes.throw(_("Same Serial No") + ": " + sr.doc.name +
_(" can not be received twice"), SerialNoDuplicateError)
if sle.actual_qty < 0:
if sr.doc.warehouse!=sle.warehouse:
webnotes.throw(_("Serial No") + ": " + serial_no +
_(" does not belong to Warehouse") + ": " + sle.warehouse,
SerialNoWarehouseError)
if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
and sr.doc.status != "Available":
webnotes.throw(_("Serial No status must be 'Available' to Deliver")
+ ": " + serial_no, SerialNoStatusError)
elif sle.actual_qty < 0:
# transfer out
webnotes.throw(_("Serial No must exist to transfer out.") + \
": " + serial_no, SerialNoNotExistsError)
elif not item_det.serial_no_series:
webnotes.throw(_("Serial Number Required for Serialized Item" + ": "
+ sle.item_code), SerialNoRequiredError)
def update_serial_nos(sle, item_det):
if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no)
for serial_no in serial_nos:
if webnotes.conn.exists("Serial No", serial_no):
sr = webnotes.bean("Serial No", serial_no)
sr.make_controller().via_stock_ledger = True
sr.doc.warehouse = sle.warehouse if sle.actual_qty > 0 else None
sr.save()
elif sle.actual_qty > 0:
make_serial_no(serial_no, sle)
elif sle.actual_qty > 0 and item_det.serial_no_series:
from webnotes.model.doc import make_autoname
serial_nos = []
for i in xrange(cint(sle.actual_qty)):
serial_nos.append(make_serial_no(make_autoname(item_det.serial_no_series), sle))
sle.serial_no = "\n".join(serial_nos)
def get_item_details(item_code):
return webnotes.conn.sql("""select name, has_batch_no, docstatus,
is_stock_item, has_serial_no, serial_no_series
from tabItem where name=%s""", item_code, as_dict=True)[0]
def get_serial_nos(serial_no):
return [s.strip() for s in cstr(serial_no).strip().replace(',', '\n').split('\n') if s.strip()]
def make_serial_no(serial_no, sle):
sr = webnotes.new_bean("Serial No")
sr.doc.serial_no = serial_no
sr.doc.item_code = sle.item_code
sr.make_controller().via_stock_ledger = True
sr.insert()
sr.doc.warehouse = sle.warehouse
sr.doc.status = "Available"
sr.save()
webnotes.msgprint(_("Serial No created") + ": " + sr.doc.name)
return sr.doc.name
def update_serial_nos_after_submit(controller, parenttype, parentfield):
if not hasattr(webnotes, "new_stock_ledger_entries"):
return
for d in controller.doclist.get({"parentfield": parentfield}):
serial_no = None
for sle in webnotes.new_stock_ledger_entries:
if sle.voucher_detail_no==d.name:
serial_no = sle.serial_no
break
if d.serial_no != serial_no:
d.serial_no = serial_no
webnotes.conn.set_value(d.doctype, d.name, "serial_no", serial_no)

View File

@@ -52,13 +52,15 @@ class DocType(StockController):
def on_submit(self): def on_submit(self):
self.update_stock_ledger() self.update_stock_ledger()
self.update_serial_no(1)
from stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
update_serial_nos_after_submit(self, "Stock Entry", "mtn_details")
self.update_production_order(1) self.update_production_order(1)
self.make_gl_entries() self.make_gl_entries()
def on_cancel(self): def on_cancel(self):
self.update_stock_ledger() self.update_stock_ledger()
self.update_serial_no(0)
self.update_production_order(0) self.update_production_order(0)
self.make_cancel_gl_entries() self.make_cancel_gl_entries()
@@ -295,24 +297,6 @@ class DocType(StockController):
select name from `tabStock Entry` where `%s`=%s and docstatus=1) select name from `tabStock Entry` where `%s`=%s and docstatus=1)
group by item_code""" % (ref_fieldname, "%s"), (self.doc.fields.get(ref_fieldname),))) group by item_code""" % (ref_fieldname, "%s"), (self.doc.fields.get(ref_fieldname),)))
def update_serial_no(self, is_submit):
"""Create / Update Serial No"""
from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
update_serial_nos_after_submit(self, "Stock Entry", "mtn_details")
for d in getlist(self.doclist, 'mtn_details'):
for serial_no in get_serial_nos(d.serial_no):
if self.doc.purpose == 'Purchase Return':
sr = webnotes.bean("Serial No", serial_no)
sr.doc.status = "Purchase Returned" if is_submit else "Available"
sr.save()
if self.doc.purpose == "Sales Return":
sr = webnotes.bean("Serial No", serial_no)
sr.doc.status = "Sales Returned" if is_submit else "Delivered"
sr.save()
def update_stock_ledger(self): def update_stock_ledger(self):
sl_entries = [] sl_entries = []
for d in getlist(self.doclist, 'mtn_details'): for d in getlist(self.doclist, 'mtn_details'):

View File

@@ -7,7 +7,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes, unittest import webnotes, unittest
from webnotes.utils import flt from webnotes.utils import flt
from stock.doctype.stock_ledger_entry.stock_ledger_entry import * from stock.doctype.serial_no.serial_no import *
from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
@@ -48,7 +48,7 @@ class TestStockEntry(unittest.TestCase):
webnotes.bean("Profile", "test2@example.com").get_controller()\ webnotes.bean("Profile", "test2@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager") .add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany from stock.utils import InvalidWarehouseCompany
st1 = webnotes.bean(copy=test_records[0]) st1 = webnotes.bean(copy=test_records[0])
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1" st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
st1.insert() st1.insert()
@@ -721,6 +721,18 @@ class TestStockEntry(unittest.TestCase):
se.insert() se.insert()
self.assertRaises(SerialNoNotExistsError, se.submit) self.assertRaises(SerialNoNotExistsError, se.submit)
def test_serial_duplicate(self):
self._clear_stock_account_balance()
self.test_serial_by_series()
se = webnotes.bean(copy=test_records[0])
se.doclist[1].item_code = "_Test Serialized Item With Series"
se.doclist[1].qty = 1
se.doclist[1].serial_no = "ABCD00001"
se.doclist[1].transfer_qty = 1
se.insert()
self.assertRaises(SerialNoDuplicateError, se.submit)
def test_serial_by_series(self): def test_serial_by_series(self):
self._clear_stock_account_balance() self._clear_stock_account_balance()
se = make_serialized_item() se = make_serialized_item()

View File

@@ -3,29 +3,17 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes import _, msgprint, ValidationError from webnotes import _, msgprint
from webnotes.utils import cint, flt, getdate, cstr from webnotes.utils import cint, flt, getdate, cstr
from webnotes.model.controller import DocListController from webnotes.model.controller import DocListController
class InvalidWarehouseCompany(ValidationError): pass
class SerialNoNotRequiredError(ValidationError): pass
class SerialNoRequiredError(ValidationError): pass
class SerialNoQtyError(ValidationError): pass
class SerialNoItemError(ValidationError): pass
class SerialNoWarehouseError(ValidationError): pass
class SerialNoStatusError(ValidationError): pass
class SerialNoNotExistsError(ValidationError): pass
def get_serial_nos(serial_no):
return [s.strip() for s in cstr(serial_no).strip().replace(',', '\n').split('\n') if s.strip()]
class DocType(DocListController): class DocType(DocListController):
def __init__(self, doc, doclist=[]): def __init__(self, doc, doclist=[]):
self.doc = doc self.doc = doc
self.doclist = doclist self.doclist = doclist
def validate(self): def validate(self):
from stock.utils import validate_warehouse_user from stock.utils import validate_warehouse_user, validate_warehouse_company
if not hasattr(webnotes, "new_stock_ledger_entries"): if not hasattr(webnotes, "new_stock_ledger_entries"):
webnotes.new_stock_ledger_entries = [] webnotes.new_stock_ledger_entries = []
@@ -33,7 +21,7 @@ class DocType(DocListController):
self.validate_mandatory() self.validate_mandatory()
self.validate_item() self.validate_item()
validate_warehouse_user(self.doc.warehouse) validate_warehouse_user(self.doc.warehouse)
self.validate_warehouse_company() validate_warehouse_company(self.doc.warehouse, self.doc.company)
self.scrub_posting_time() self.scrub_posting_time()
from accounts.utils import validate_fiscal_year from accounts.utils import validate_fiscal_year
@@ -42,7 +30,9 @@ class DocType(DocListController):
def on_submit(self): def on_submit(self):
self.check_stock_frozen_date() self.check_stock_frozen_date()
self.actual_amt_check() self.actual_amt_check()
self.validate_serial_no()
from stock.doctype.serial_no.serial_no import process_serial_no
process_serial_no(self.doc)
#check for item quantity available in stock #check for item quantity available in stock
def actual_amt_check(self): def actual_amt_check(self):
@@ -64,13 +54,6 @@ class DocType(DocListController):
sself.doc.fields.pop('batch_bal') sself.doc.fields.pop('batch_bal')
def validate_warehouse_company(self):
warehouse_company = webnotes.conn.get_value("Warehouse", self.doc.warehouse, "company")
if warehouse_company and warehouse_company != self.doc.company:
webnotes.msgprint(_("Warehouse does not belong to company.") + " (" + \
self.doc.warehouse + ", " + self.doc.company +")",
raise_exception=InvalidWarehouseCompany)
def validate_mandatory(self): def validate_mandatory(self):
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company'] mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
for k in mandatory: for k in mandatory:
@@ -81,7 +64,10 @@ class DocType(DocListController):
msgprint("Warehouse: '%s' does not exist in the system. Please check." % self.doc.fields.get(k), raise_exception = 1) msgprint("Warehouse: '%s' does not exist in the system. Please check." % self.doc.fields.get(k), raise_exception = 1)
def validate_item(self): def validate_item(self):
item_det = self.get_item_details() item_det = webnotes.conn.sql("""select name, has_batch_no, docstatus,
is_stock_item, has_serial_no, serial_no_series
from tabItem where name=%s""",
self.doc.item_code, as_dict=True)[0]
if item_det.is_stock_item != 'Yes': if item_det.is_stock_item != 'Yes':
webnotes.throw("""Item: "%s" is not a Stock Item.""" % self.doc.item_code) webnotes.throw("""Item: "%s" is not a Stock Item.""" % self.doc.item_code)
@@ -99,95 +85,6 @@ class DocType(DocListController):
if not self.doc.stock_uom: if not self.doc.stock_uom:
self.doc.stock_uom = item_det.stock_uom self.doc.stock_uom = item_det.stock_uom
def get_item_details(self):
return webnotes.conn.sql("""select name, has_batch_no, docstatus,
is_stock_item, has_serial_no, serial_no_series
from tabItem where name=%s""",
self.doc.item_code, as_dict=True)[0]
def validate_serial_no(self):
item_det = self.get_item_details()
if item_det.has_serial_no=="No":
if self.doc.serial_no:
webnotes.throw(_("Serial Number should be blank for Non Serialized Item" + ": " + self.doc.item),
SerialNoNotRequiredError)
else:
if self.doc.serial_no:
serial_nos = get_serial_nos(self.doc.serial_no)
if cint(self.doc.actual_qty) != flt(self.doc.actual_qty):
webnotes.throw(_("Serial No qty cannot be a fraction") + \
(": %s (%s)" % (self.doc.item_code, self.doc.actual_qty)))
if len(serial_nos) and len(serial_nos) != abs(cint(self.doc.actual_qty)):
webnotes.throw(_("Serial Nos do not match with qty") + \
(": %s (%s)" % (self.doc.item_code, self.doc.actual_qty)), SerialNoQtyError)
# check serial no exists, if yes then source
for serial_no in serial_nos:
if webnotes.conn.exists("Serial No", serial_no):
sr = webnotes.bean("Serial No", serial_no)
if sr.doc.item_code!=self.doc.item_code:
webnotes.throw(_("Serial No does not belong to Item") + \
(": %s (%s)" % (self.doc.item_code, serial_no)), SerialNoItemError)
sr.make_controller().via_stock_ledger = True
if self.doc.actual_qty < 0:
if sr.doc.warehouse!=self.doc.warehouse:
webnotes.throw(_("Serial No") + ": " + serial_no +
_(" does not belong to Warehouse") + ": " + self.doc.warehouse,
SerialNoWarehouseError)
if self.doc.voucher_type in ("Delivery Note", "Sales Invoice") \
and sr.doc.status != "Available":
webnotes.throw(_("Serial No status must be 'Available' to Deliver")
+ ": " + serial_no, SerialNoStatusError)
sr.doc.warehouse = None
sr.save()
else:
sr.doc.warehouse = self.doc.warehouse
sr.save()
else:
if self.doc.actual_qty < 0:
# transfer out
webnotes.throw(_("Serial No must exist to transfer out.") + \
": " + serial_no, SerialNoNotExistsError)
else:
# transfer in
self.make_serial_no(serial_no)
else:
if item_det.serial_no_series:
from webnotes.model.doc import make_autoname
serial_nos = []
for i in xrange(cint(self.doc.actual_qty)):
serial_nos.append(self.make_serial_no(make_autoname(item_det.serial_no_series)))
self.doc.serial_no = "\n".join(serial_nos)
else:
webnotes.throw(_("Serial Number Required for Serialized Item" + ": " + self.doc.item_code),
SerialNoRequiredError)
def make_serial_no(self, serial_no):
sr = webnotes.new_bean("Serial No")
sr.doc.serial_no = serial_no
sr.doc.item_code = self.doc.item_code
sr.doc.purchase_rate = self.doc.incoming_rate
sr.doc.purchase_document_type = self.doc.voucher_type
sr.doc.purchase_document_no = self.doc.voucher_no
sr.doc.purchase_date = self.doc.posting_date
sr.doc.purchase_time = self.doc.posting_time
sr.make_controller().via_stock_ledger = True
sr.insert()
# set warehouse
sr.doc.warehouse = self.doc.warehouse
sr.doc.status = "Available"
sr.save()
webnotes.msgprint(_("Serial No created") + ": " + sr.doc.name)
return sr.doc.name
def check_stock_frozen_date(self): def check_stock_frozen_date(self):
stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or '' stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
if stock_frozen_upto: if stock_frozen_upto:
@@ -199,21 +96,6 @@ class DocType(DocListController):
if not self.doc.posting_time or self.doc.posting_time == '00:0': if not self.doc.posting_time or self.doc.posting_time == '00:0':
self.doc.posting_time = '00:00' self.doc.posting_time = '00:00'
def update_serial_nos_after_submit(controller, parenttype, parentfield):
if not hasattr(webnotes, "new_stock_ledger_entries"):
return
for d in controller.doclist.get({"parentfield": parentfield}):
serial_no = None
for sle in webnotes.new_stock_ledger_entries:
if sle.voucher_detail_no==d.name:
serial_no = sle.serial_no
break
if d.serial_no != serial_no:
d.serial_no = serial_no
webnotes.conn.set_value(d.doctype, d.name, "serial_no", serial_no)
def on_doctype_update(): def on_doctype_update():
if not webnotes.conn.sql("""show index from `tabStock Ledger Entry` if not webnotes.conn.sql("""show index from `tabStock Ledger Entry`
where Key_name="posting_sort_index" """): where Key_name="posting_sort_index" """):

View File

@@ -9,6 +9,7 @@ from webnotes.defaults import get_global_default
from webnotes.utils.email_lib import sendmail from webnotes.utils.email_lib import sendmail
class UserNotAllowedForWarehouse(webnotes.ValidationError): pass class UserNotAllowedForWarehouse(webnotes.ValidationError): pass
class InvalidWarehouseCompany(webnotes.ValidationError): pass
def get_stock_balance_on(warehouse, posting_date=None): def get_stock_balance_on(warehouse, posting_date=None):
if not posting_date: posting_date = nowdate() if not posting_date: posting_date = nowdate()
@@ -217,6 +218,12 @@ def validate_warehouse_user(warehouse):
webnotes.throw(_("Not allowed entry in Warehouse") \ webnotes.throw(_("Not allowed entry in Warehouse") \
+ ": " + warehouse, UserNotAllowedForWarehouse) + ": " + warehouse, UserNotAllowedForWarehouse)
def validate_warehouse_company(warehouse, company):
warehouse_company = webnotes.conn.get_value("Warehouse", warehouse, "company")
if warehouse_company and warehouse_company != company:
webnotes.msgprint(_("Warehouse does not belong to company.") + " (" + \
warehouse + ", " + company +")", raise_exception=InvalidWarehouseCompany)
def get_sales_bom_buying_amount(item_code, warehouse, voucher_type, voucher_no, voucher_detail_no, def get_sales_bom_buying_amount(item_code, warehouse, voucher_type, voucher_no, voucher_detail_no,
stock_ledger_entries, item_sales_bom): stock_ledger_entries, item_sales_bom):
# sales bom item # sales bom item

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-05-06 14:25:21", "creation": "2013-05-06 14:25:21",
"docstatus": 0, "docstatus": 0,
"modified": "2013-05-06 14:32:47", "modified": "2013-10-09 12:23:27",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -10,7 +10,7 @@
"doctype": "Report", "doctype": "Report",
"is_standard": "Yes", "is_standard": "Yes",
"name": "__common__", "name": "__common__",
"query": "SELECT\n ms_item.scheduled_date as \"Schedule Date:Date:120\",\n\tms_item.item_code as \"Item Code:Link/Item:120\",\n\tms_item.item_name as \"Item Name::120\",\n\tms_item.serial_no as \"Serial No::120\",\n\tms_item.incharge_name as \"Incharge::120\",\n\tms.customer_name as \"Customer:Link/Customer:120\",\n\tms.address_display as \"Customer Address::120\",\n\tms.sales_order_no as \"Sales Order:Link/Sales Order:120\",\n\tms.company as \"Company:Link/Company:120\"\n\t\nFROM\n\t`tabMaintenance Schedule` ms, `tabMaintenance Schedule Detail` ms_item\nWHERE\n\tms.name = ms_item.parent and ms.docstatus = 1\nORDER BY\n\tms_item.scheduled_date asc, ms_item.item_code asc", "query": "SELECT\n ms_sch.scheduled_date as \"Schedule Date:Date:120\",\n\tms_sch.item_code as \"Item Code:Link/Item:120\",\n\tms_sch.item_name as \"Item Name::120\",\n\tms_sch.serial_no as \"Serial No::120\",\n\tms_sch.incharge_name as \"Incharge::120\",\n\tms.customer_name as \"Customer:Link/Customer:120\",\n\tms.address_display as \"Customer Address::120\",\n\tms_item.prevdoc_docname as \"Sales Order:Link/Sales Order:120\",\n\tms.company as \"Company:Link/Company:120\"\n\t\nFROM\n\t`tabMaintenance Schedule` ms, \n `tabMaintenance Schedule Detail` ms_sch, \n `tabMaintenance Schedule Item` ms_item\nWHERE\n\tms.name = ms_sch.parent and ms.name = ms_item.parent and ms.docstatus = 1\nORDER BY\n\tms_sch.scheduled_date asc, ms_sch.item_code asc",
"ref_doctype": "Maintenance Schedule", "ref_doctype": "Maintenance Schedule",
"report_name": "Maintenance Schedules", "report_name": "Maintenance Schedules",
"report_type": "Query Report" "report_type": "Query Report"