Merge branch 'wsgi' of https://github.com/webnotes/erpnext into i18n

Conflicts:
	accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
	accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
	public/js/complete_setup.js
	selling/doctype/opportunity/opportunity.js
	selling/doctype/quotation/quotation.js
This commit is contained in:
Bárbara Perretti
2013-10-16 15:09:18 -03:00
250 changed files with 5061 additions and 2506 deletions

View File

@@ -5,7 +5,6 @@ from __future__ import unicode_literals
import webnotes
from webnotes.utils import add_days, cint,flt, nowdate, get_url_to_form, formatdate
from webnotes import msgprint, _
sql = webnotes.conn.sql
import webnotes.defaults
@@ -16,7 +15,7 @@ class DocType:
self.doclist = doclist
def validate(self):
if not self.doc.stock_uom:
if self.doc.fields.get("__islocal") or not self.doc.stock_uom:
self.doc.stock_uom = webnotes.conn.get_value('Item', self.doc.item_code, 'stock_uom')
self.validate_mandatory()
@@ -61,7 +60,7 @@ class DocType:
self.doc.save()
def get_first_sle(self):
sle = sql("""
sle = webnotes.conn.sql("""
select * from `tabStock Ledger Entry`
where item_code = %s
and warehouse = %s

View File

@@ -10,6 +10,7 @@ cur_frm.cscript.sales_team_fname = "sales_team";
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/selling/doctype/sales_common/sales_common.js');
wn.require('app/accounts/doctype/sales_invoice/pos.js');
wn.provide("erpnext.stock");
erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({
@@ -21,7 +22,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
var from_sales_invoice = false;
from_sales_invoice = cur_frm.get_doclist({parentfield: "delivery_note_details"})
.some(function(item) {
return item.prevdoc_doctype==="Sales Invoice" ? true : false;
return item.against_sales_invoice ? true : false;
});
if(!from_sales_invoice)
@@ -180,12 +181,12 @@ cur_frm.pformat.sales_order_no= function(doc, cdt, cdn){
if(cl.length){
prevdoc_list = new Array();
for(var i=0;i<cl.length;i++){
if(cl[i].prevdoc_doctype == 'Sales Order' && cl[i].prevdoc_docname && prevdoc_list.indexOf(cl[i].prevdoc_docname) == -1) {
prevdoc_list.push(cl[i].prevdoc_docname);
if(cl[i].against_sales_order && prevdoc_list.indexOf(cl[i].against_sales_order) == -1) {
prevdoc_list.push(cl[i].against_sales_order);
if(prevdoc_list.length ==1)
out += make_row(cl[i].prevdoc_doctype, cl[i].prevdoc_docname, cl[i].prevdoc_date,0);
out += make_row("Sales Order", cl[i].against_sales_order, null, 0);
else
out += make_row('', cl[i].prevdoc_docname, cl[i].prevdoc_date,0);
out += make_row('', cl[i].against_sales_order, null,0);
}
}
}

View File

@@ -28,7 +28,7 @@ class DocType(SellingController):
'target_parent_field': 'per_delivered',
'target_ref_field': 'qty',
'source_field': 'qty',
'percent_join_field': 'prevdoc_docname',
'percent_join_field': 'against_sales_order',
'status_field': 'delivery_status',
'keyword': 'Delivered'
}]
@@ -66,14 +66,11 @@ class DocType(SellingController):
"""Re-calculates Basic Rate & amount based on Price List Selected"""
get_obj('Sales Common').get_adj_percent(self)
def get_rate(self,arg):
return get_obj('Sales Common').get_rate(arg)
def so_required(self):
"""check in manage account if sales order required or not"""
if webnotes.conn.get_value("Selling Settings", None, 'so_required') == 'Yes':
for d in getlist(self.doclist,'delivery_note_details'):
if not d.prevdoc_docname:
if not d.against_sales_order:
msgprint("Sales Order No. required against item %s"%d.item_code)
raise Exception
@@ -89,7 +86,6 @@ class DocType(SellingController):
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
sales_com_obj.check_active_sales_items(self)
sales_com_obj.get_prevdoc_date(self)
self.validate_for_items()
self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "qty")
@@ -106,26 +102,27 @@ class DocType(SellingController):
if not self.doc.installation_status: self.doc.installation_status = 'Not Installed'
def validate_with_previous_doc(self):
prev_doctype = [d.prevdoc_doctype for d in self.doclist.get({
"parentfield": "delivery_note_details"}) if cstr(d.prevdoc_doctype) != ""]
if prev_doctype:
super(DocType, self).validate_with_previous_doc(self.tname, {
prev_doctype[0]: {
"ref_dn_field": "prevdoc_docname",
"compare_fields": [["customer", "="], ["company", "="], ["project_name", "="],
["currency", "="]],
},
})
if cint(webnotes.defaults.get_global_default('maintain_same_sales_rate')):
items = self.doclist.get({"parentfield": "delivery_note_details"})
for fn in (("Sales Order", "against_sales_order"), ("Sales Invoice", "against_sales_invoice")):
if items.get_distinct_values(fn[1]):
super(DocType, self).validate_with_previous_doc(self.tname, {
prev_doctype[0] + " Item": {
"ref_dn_field": "prevdoc_detail_docname",
"compare_fields": [["export_rate", "="]],
"is_child_table": True
}
fn[0]: {
"ref_dn_field": fn[1],
"compare_fields": [["customer", "="], ["company", "="], ["project_name", "="],
["currency", "="]],
},
})
if cint(webnotes.defaults.get_global_default('maintain_same_sales_rate')):
super(DocType, self).validate_with_previous_doc(self.tname, {
fn[0] + " Item": {
"ref_dn_field": "prevdoc_detail_docname",
"compare_fields": [["export_rate", "="]],
"is_child_table": True
}
})
def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""
if self.doc.project_name and self.doc.customer:
@@ -137,8 +134,8 @@ class DocType(SellingController):
def validate_for_items(self):
check_list, chk_dupl_itm = [], []
for d in getlist(self.doclist,'delivery_note_details'):
e = [d.item_code, d.description, d.warehouse, d.prevdoc_docname or '', d.batch_no or '']
f = [d.item_code, d.description, d.prevdoc_docname or '']
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == 'Yes':
if e in check_list:
@@ -185,7 +182,6 @@ class DocType(SellingController):
# create stock ledger entry
self.update_stock_ledger()
self.update_serial_nos()
self.credit_limit()
@@ -203,42 +199,12 @@ class DocType(SellingController):
self.update_prevdoc_status()
self.update_stock_ledger()
self.update_serial_nos(cancel=True)
webnotes.conn.set(self.doc, 'status', 'Cancelled')
self.cancel_packing_slips()
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):
"""
Validate that if packed qty exists, it should be equal to qty
@@ -323,7 +289,7 @@ class DocType(SellingController):
"""check credit limit of items in DN Detail which are not fetched from sales order"""
amount, total = 0, 0
for d in getlist(self.doclist, 'delivery_note_details'):
if not d.prevdoc_docname:
if not (d.against_sales_order or d.against_sales_invoice):
amount += d.amount
if amount != 0:
total = (amount/self.doc.net_total)*self.doc.grand_total
@@ -375,7 +341,7 @@ def make_sales_invoice(source_name, target_doclist=None):
"name": "dn_detail",
"parent": "delivery_note",
"prevdoc_detail_docname": "so_detail",
"prevdoc_docname": "sales_order",
"against_sales_order": "sales_order",
"serial_no": "serial_no"
},
"postprocess": update_item
@@ -399,7 +365,6 @@ def make_sales_invoice(source_name, target_doclist=None):
def make_installation_note(source_name, target_doclist=None):
def update_item(obj, target, source_parent):
target.qty = flt(obj.qty) - flt(obj.installed_qty)
target.prevdoc_date = source_parent.posting_date
target.serial_no = obj.serial_no
doclist = get_mapped_doclist("Delivery Note", source_name, {

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 19:29:09",
"docstatus": 0,
"modified": "2013-08-09 14:46:32",
"modified": "2013-10-11 13:19:40",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -230,7 +230,7 @@
"depends_on": "eval:doc.po_no",
"doctype": "DocField",
"fieldname": "po_date",
"fieldtype": "Data",
"fieldtype": "Date",
"hidden": 1,
"label": "Customer's Purchase Order Date",
"no_copy": 0,
@@ -263,7 +263,6 @@
"reqd": 1
},
{
"default": "1.00",
"description": "Rate at which customer's currency is converted to company's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",

View File

@@ -9,20 +9,22 @@ import webnotes.defaults
from webnotes.utils import cint
from stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries, set_perpetual_inventory, test_records as pr_test_records
def _insert_purchase_receipt(item_code=None):
if not item_code:
item_code = pr_test_records[0][1]["item_code"]
pr = webnotes.bean(copy=pr_test_records[0])
pr.doclist[1].item_code = item_code
pr.insert()
pr.submit()
class TestDeliveryNote(unittest.TestCase):
def _insert_purchase_receipt(self, item_code=None):
pr = webnotes.bean(copy=pr_test_records[0])
if item_code:
pr.doclist[1].item_code = item_code
pr.insert()
pr.submit()
def test_over_billing_against_dn(self):
self.clear_stock_account_balance()
self._insert_purchase_receipt()
_insert_purchase_receipt()
from stock.doctype.delivery_note.delivery_note import make_sales_invoice
self._insert_purchase_receipt()
_insert_purchase_receipt()
dn = webnotes.bean(copy=test_records[0]).insert()
self.assertRaises(webnotes.ValidationError, make_sales_invoice,
@@ -44,7 +46,7 @@ class TestDeliveryNote(unittest.TestCase):
set_perpetual_inventory(0)
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 0)
self._insert_purchase_receipt()
_insert_purchase_receipt()
dn = webnotes.bean(copy=test_records[0])
dn.insert()
@@ -69,7 +71,7 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1)
webnotes.conn.set_value("Item", "_Test Item", "valuation_method", "FIFO")
self._insert_purchase_receipt()
_insert_purchase_receipt()
dn = webnotes.bean(copy=test_records[0])
dn.doclist[1].expense_account = "Cost of Goods Sold - _TC"
@@ -123,8 +125,8 @@ class TestDeliveryNote(unittest.TestCase):
self.clear_stock_account_balance()
set_perpetual_inventory()
self._insert_purchase_receipt()
self._insert_purchase_receipt("_Test Item Home Desktop 100")
_insert_purchase_receipt()
_insert_purchase_receipt("_Test Item Home Desktop 100")
dn = webnotes.bean(copy=test_records[0])
dn.doclist[1].item_code = "_Test Sales BOM Item"
@@ -160,7 +162,7 @@ class TestDeliveryNote(unittest.TestCase):
def test_serialized(self):
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
from stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item()
serial_nos = get_serial_nos(se.doclist[1].serial_no)
@@ -180,7 +182,7 @@ class TestDeliveryNote(unittest.TestCase):
return dn
def test_serialized_cancel(self):
from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
from stock.doctype.serial_no.serial_no import get_serial_nos
dn = self.test_serialized()
dn.cancel()
@@ -192,7 +194,7 @@ class TestDeliveryNote(unittest.TestCase):
"delivery_document_no"))
def test_serialize_status(self):
from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoStatusError, get_serial_nos
from stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
se = make_serialized_item()

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-04-22 13:15:44",
"docstatus": 0,
"modified": "2013-08-29 16:58:16",
"modified": "2013-10-10 17:03:11",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -350,48 +350,17 @@
},
{
"doctype": "DocField",
"fieldname": "prevdoc_doctype",
"fieldtype": "Data",
"hidden": 1,
"in_filter": 1,
"label": "Document Type",
"no_copy": 1,
"oldfieldname": "prevdoc_doctype",
"oldfieldtype": "Data",
"print_hide": 1,
"print_width": "150px",
"read_only": 1,
"search_index": 1,
"width": "150px"
"fieldname": "against_sales_order",
"fieldtype": "Link",
"label": "Against Sales Order",
"options": "Sales Order"
},
{
"doctype": "DocField",
"fieldname": "prevdoc_docname",
"fieldtype": "Data",
"hidden": 0,
"in_filter": 1,
"label": "Against Document No",
"no_copy": 1,
"oldfieldname": "prevdoc_docname",
"oldfieldtype": "Data",
"print_hide": 1,
"print_width": "150px",
"read_only": 1,
"search_index": 1,
"width": "150px"
},
{
"doctype": "DocField",
"fieldname": "prevdoc_date",
"fieldtype": "Date",
"hidden": 1,
"in_filter": 1,
"label": "Against Document Date",
"no_copy": 1,
"oldfieldname": "prevdoc_date",
"oldfieldtype": "Date",
"print_hide": 1,
"read_only": 1
"fieldname": "against_sales_invoice",
"fieldtype": "Link",
"label": "Against Sales Invoice",
"options": "Sales Invoice"
},
{
"doctype": "DocField",
@@ -420,6 +389,17 @@
"print_hide": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "buying_amount",
"fieldtype": "Currency",
"hidden": 1,
"label": "Buying Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"doctype": "DocField",

View File

@@ -11,14 +11,16 @@ cur_frm.cscript.refresh = function(doc) {
&& doc.__islocal)
cur_frm.toggle_display("item_code", sys_defaults.item_naming_by!="Naming Series"
&& doc.__islocal)
if(!doc.__islocal && doc.show_in_website) {
cur_frm.add_custom_button("View In Website", function() {
window.open(doc.page_name);
}, "icon-globe");
}
if ((!doc.__islocal) && (doc.is_stock_item == 'Yes')) {
var callback = function(r, rt) {
var enabled = (r.message == 'exists') ? false : true;
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'], enabled);
}
return $c_obj(make_doclist(doc.doctype, doc.name),'check_if_sle_exists','',callback);
if (!doc.__islocal && doc.is_stock_item == 'Yes') {
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'],
doc.__sle_exists=="exists" ? false : true);
}
}

View File

@@ -14,6 +14,9 @@ from webnotes.model.controller import DocListController
class WarehouseNotSet(Exception): pass
class DocType(DocListController):
def onload(self):
self.doc.fields["__sle_exists"] = self.check_if_sle_exists()
def autoname(self):
if webnotes.conn.get_default("item_naming_by")=="Naming Series":
from webnotes.model.doc import make_autoname

View File

@@ -31,7 +31,7 @@ test_records = [
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_pro_applicable": "Yes",
"is_sub_contracted_item": "No",
"stock_uom": "_Test UOM",
"default_income_account": "Sales - _TC",
@@ -45,6 +45,26 @@ test_records = [
"material_request_type": "Purchase"
},
],
[{
"doctype": "Item",
"item_code": "_Test Item 2",
"item_name": "_Test Item 2",
"description": "_Test Item 2",
"item_group": "_Test Item Group",
"is_stock_item": "Yes",
"is_asset_item": "No",
"has_batch_no": "No",
"has_serial_no": "No",
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
"is_pro_applicable": "Yes",
"is_sub_contracted_item": "No",
"stock_uom": "_Test UOM",
"default_income_account": "Sales - _TC",
"default_warehouse": "_Test Warehouse - _TC",
}],
[{
"doctype": "Item",
"item_code": "_Test Item Home Desktop 100",
@@ -63,6 +83,7 @@ test_records = [
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
"is_manufactured_item": "No",
"stock_uom": "_Test UOM"
},
{
@@ -88,6 +109,7 @@ test_records = [
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
"is_manufactured_item": "No",
"stock_uom": "_Test UOM"
}],
[{
@@ -182,8 +204,49 @@ test_records = [
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_pro_applicable": "Yes",
"is_sub_contracted_item": "No",
"stock_uom": "_Test UOM"
}],
[{
"doctype": "Item",
"item_code": "_Test Item Home Desktop Manufactured",
"item_name": "_Test Item Home Desktop Manufactured",
"description": "_Test Item Home Desktop Manufactured",
"item_group": "_Test Item Group Desktops",
"default_warehouse": "_Test Warehouse - _TC",
"default_income_account": "Sales - _TC",
"is_stock_item": "Yes",
"is_asset_item": "No",
"has_batch_no": "No",
"has_serial_no": "No",
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
"is_pro_applicable": "Yes",
"is_sub_contracted_item": "No",
"is_manufactured_item": "Yes",
"stock_uom": "_Test UOM"
}],
[{
"doctype": "Item",
"item_code": "_Test FG Item 2",
"item_name": "_Test FG Item 2",
"description": "_Test FG Item 2",
"item_group": "_Test Item Group Desktops",
"is_stock_item": "Yes",
"default_warehouse": "_Test Warehouse - _TC",
"default_income_account": "Sales - _TC",
"is_asset_item": "No",
"has_batch_no": "No",
"has_serial_no": "No",
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
"is_pro_applicable": "Yes",
"is_sub_contracted_item": "Yes",
"stock_uom": "_Test UOM"
}],
]

View File

@@ -85,7 +85,6 @@ class DocType:
pr_bean = webnotes.bean("Purchase Receipt", pr)
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`
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.run_method("update_ordered_qty")
pr_bean.run_method("update_stock")
pr_bean.run_method("update_serial_nos")
pr_bean.run_method("make_gl_entries")

View File

@@ -21,7 +21,11 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
+ wn._("Fulfilled"), cint(doc.per_ordered));
}
if(doc.docstatus == 1 && doc.status != 'Stopped'){
if(doc.docstatus==0) {
cur_frm.add_custom_button(wn._("Get Items from BOM"), cur_frm.cscript.get_items_from_bom, "icon-sitemap");
}
if(doc.docstatus == 1 && doc.status != 'Stopped') {
if(doc.material_request_type === "Purchase")
cur_frm.add_custom_button(wn._("Make Supplier Quotation"),
this.make_supplier_quotation);
@@ -63,6 +67,53 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
},
schedule_date: function(doc, cdt, cdn) {
var val = locals[cdt][cdn].schedule_date;
if(val) {
$.each(wn.model.get("Material Request Item", { parent: cur_frm.doc.name }), function(i, d) {
if(!d.schedule_date) {
d.schedule_date = val;
}
});
refresh_field("indent_details");
}
},
get_items_from_bom: function() {
var d = new wn.ui.Dialog({
title: wn._("Get Items from BOM"),
fields: [
{"fieldname":"bom", "fieldtype":"Link", "label":wn._("BOM"),
options:"BOM"},
{"fieldname":"fetch_exploded", "fieldtype":"Check",
"label":wn._("Fetch exploded BOM (including sub-assemblies)"), "default":1},
{fieldname:"fetch", "label":wn._("Get Items from BOM"), "fieldtype":"Button"}
]
});
d.get_input("fetch").on("click", function() {
var values = d.get_values();
if(!values) return;
wn.call({
method:"manufacturing.doctype.bom.bom.get_bom_items",
args: values,
callback: function(r) {
$.each(r.message, function(i, item) {
var d = wn.model.add_child(cur_frm.doc, "Material Request Item", "indent_details");
d.item_code = item.item_code;
d.description = item.description;
d.warehouse = item.default_warehouse;
d.uom = item.stock_uom;
d.qty = item.qty;
});
d.hide();
refresh_field("indent_details");
}
});
});
d.show();
},
tc_name: function() {
this.get_terms();
},

View File

@@ -20,10 +20,6 @@ class DocType(BuyingController):
self.tname = 'Material Request Item'
self.fname = 'indent_details'
# get available qty at warehouse
def get_bin_details(self, arg = ''):
return get_obj(dt='Purchase Common').get_bin_details(arg)
def check_if_already_pulled(self):
pass#if self.[d.sales_order_no for d in getlist(self.doclist, 'indent_details')]
@@ -68,22 +64,14 @@ class DocType(BuyingController):
self.doc.status = "Draft"
import utilities
utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped",
"Cancelled"])
utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", "Cancelled"])
# restrict material request type
self.validate_value("material_request_type", "in", ["Purchase", "Transfer"])
# Get Purchase Common Obj
pc_obj = get_obj(dt='Purchase Common')
# Validate for items
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):
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""

View File

@@ -2,12 +2,13 @@
{
"creation": "2013-03-07 14:48:38",
"docstatus": 0,
"modified": "2013-08-08 14:22:27",
"modified": "2013-10-02 14:24:42",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
"allow_import": 1,
"allow_print": 0,
"autoname": "naming_series:",
"doctype": "DocType",

View File

@@ -315,10 +315,10 @@ class TestMaterialRequest(unittest.TestCase):
self.assertRaises(webnotes.MappingMismatchError, se.insert)
def test_warehouse_company_validation(self):
from controllers.buying_controller import WrongWarehouseCompany
from stock.utils import InvalidWarehouseCompany
mr = webnotes.bean(copy=test_records[0])
mr.doc.company = "_Test Company 1"
self.assertRaises(WrongWarehouseCompany, mr.insert)
self.assertRaises(InvalidWarehouseCompany, mr.insert)
test_dependencies = ["Currency Exchange"]
test_records = [

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:28:02",
"docstatus": 0,
"modified": "2013-08-07 14:45:11",
"modified": "2013-10-11 14:21:32",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -138,7 +138,7 @@
"oldfieldname": "item_name",
"oldfieldtype": "Data",
"print_width": "100px",
"reqd": 1,
"reqd": 0,
"search_index": 1,
"width": "100px"
},

View File

@@ -8,6 +8,7 @@ cur_frm.cscript.other_fname = "purchase_tax_details";
wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/buying/doctype/purchase_common/purchase_common.js');
wn.require('app/accounts/doctype/sales_invoice/pos.js');
wn.provide("erpnext.stock");
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({

View File

@@ -7,7 +7,7 @@ import webnotes
from webnotes.utils import cstr, flt, cint
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import msgprint
from webnotes import msgprint, _
import webnotes.defaults
from stock.utils import update_bin
@@ -38,10 +38,6 @@ class DocType(BuyingController):
total_qty = sum((item.qty for item in self.doclist.get({"parentfield": "purchase_receipt_details"})))
self.doc.fields["__billing_complete"] = billed_qty[0][0] == total_qty
# get available qty at warehouse
def get_bin_details(self, arg = ''):
return get_obj(dt='Purchase Common').get_bin_details(arg)
def validate(self):
super(DocType, self).validate()
@@ -63,7 +59,6 @@ class DocType(BuyingController):
pc_obj = get_obj(dt='Purchase Common')
pc_obj.validate_for_items(self)
pc_obj.get_prevdoc_date(self)
self.check_for_stopped_status(pc_obj)
# sub-contracting
@@ -246,26 +241,12 @@ class DocType(BuyingController):
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_details")
purchase_controller.update_last_purchase_rate(self, 1)
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):
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))
@@ -292,7 +273,6 @@ class DocType(BuyingController):
self.update_ordered_qty()
self.update_stock()
self.update_serial_nos(cancel=True)
self.update_prevdoc_status()
pc_obj.update_last_purchase_rate(self, 0)
@@ -305,10 +285,6 @@ class DocType(BuyingController):
bin = webnotes.conn.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.rm_item_code, self.doc.supplier_warehouse), as_dict = 1)
d.current_stock = bin and flt(bin[0]['actual_qty']) or 0
def get_rate(self,arg):
return get_obj('Purchase Common').get_rate(arg,self)
def get_gl_entries_for_stock(self, warehouse_account=None):
against_stock_account = self.get_company_default("stock_received_but_not_billed")

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-05-21 16:16:39",
"docstatus": 0,
"modified": "2013-08-09 14:47:05",
"modified": "2013-10-11 13:20:13",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -215,7 +215,6 @@
"reqd": 1
},
{
"default": "1.00",
"description": "Rate at which supplier's currency is converted to company's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",

View File

@@ -104,6 +104,7 @@ class TestPurchaseReceipt(unittest.TestCase):
pr.doclist[1].received_qty = 1
pr.insert()
pr.submit()
self.assertEquals(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
"supplier"), pr.doc.supplier)
@@ -112,7 +113,7 @@ class TestPurchaseReceipt(unittest.TestCase):
def test_serial_no_cancel(self):
pr = self.test_serial_no_supplier()
pr.cancel()
self.assertFalse(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
"warehouse"))

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 19:29:10",
"docstatus": 0,
"modified": "2013-09-20 11:36:55",
"modified": "2013-10-10 17:02:51",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -424,19 +424,6 @@
"search_index": 1,
"width": "150px"
},
{
"doctype": "DocField",
"fieldname": "prevdoc_date",
"fieldtype": "Date",
"hidden": 1,
"in_filter": 1,
"label": "Purchase Order Date",
"no_copy": 1,
"oldfieldname": "prevdoc_date",
"oldfieldtype": "Date",
"print_hide": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "rm_supp_cost",

View File

@@ -4,30 +4,39 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import cint, getdate, nowdate
from webnotes.utils import cint, getdate, cstr, flt, add_days
import datetime
from webnotes import msgprint, _
from webnotes import msgprint, _, ValidationError
from controllers.stock_controller import StockController
class SerialNoCannotCreateDirectError(webnotes.ValidationError): pass
class SerialNoCannotCannotChangeError(webnotes.ValidationError): pass
class SerialNoCannotCreateDirectError(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):
def __init__(self, doc, doclist=[]):
def __init__(self, doc, doclist=None):
self.doc = doc
self.doclist = doclist
self.doclist = doclist or []
self.via_stock_ledger = False
def validate(self):
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"),
SerialNoCannotCreateDirectError)
webnotes.throw(_("New Serial No cannot have Warehouse. Warehouse must be \
set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
self.validate_warranty_status()
self.validate_amc_status()
self.validate_warehouse()
self.validate_item()
self.on_stock_ledger_entry()
def validate_amc_status(self):
"""
@@ -41,7 +50,8 @@ class DocType(StockController):
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())):
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):
@@ -49,10 +59,11 @@ class DocType(StockController):
item_code, warehouse = webnotes.conn.get_value("Serial No",
self.doc.name, ["item_code", "warehouse"])
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:
webnotes.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
webnotes.throw(_("Warehouse cannot be changed for Serial No."),
SerialNoCannotCannotChangeError)
def validate_item(self):
"""
@@ -67,16 +78,89 @@ class DocType(StockController):
self.doc.item_name = item.item_name
self.doc.brand = item.brand
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):
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:
webnotes.throw(_("Cannot delete Serial No in warehouse. First remove from warehouse, then delete.") + \
": " + self.doc.name)
def on_cancel(self):
self.on_trash()
webnotes.throw(_("Cannot delete Serial No in warehouse. \
First remove from warehouse, then delete.") + ": " + self.doc.name)
def on_rename(self, new, old, merge=False):
"""rename serial_no text fields"""
@@ -93,3 +177,122 @@ class DocType(StockController):
webnotes.conn.sql("""update `tab%s` set serial_no = %s
where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(serial_nos), item[0]))
def on_stock_ledger_entry(self):
if self.via_stock_ledger and not self.doc.fields.get("__islocal"):
self.set_status()
self.set_purchase_details()
self.set_sales_details()
def on_stock_ledger_entry(self):
if self.via_stock_ledger and not self.doc.fields.get("__islocal"):
self.set_status()
self.set_purchase_details()
self.set_sales_details()
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 not sle.serial_no and 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_autoname(item_det.serial_no_series))
webnotes.conn.set(sle, "serial_no", "\n".join(serial_nos))
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)
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, parentfield):
stock_ledger_entries = webnotes.conn.sql("""select voucher_detail_no, serial_no
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
(controller.doc.doctype, controller.doc.name), as_dict=True)
if not stock_ledger_entries: return
for d in controller.doclist.get({"parentfield": parentfield}):
serial_no = None
for sle in 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

@@ -6,7 +6,7 @@ import webnotes
import webnotes.defaults
from webnotes.utils import cstr, cint, flt, comma_or, nowdate
from webnotes.model.doc import Document, addchild
from webnotes.model.doc import addchild
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import msgprint, _
@@ -15,12 +15,12 @@ from stock.stock_ledger import get_previous_sle
from controllers.queries import get_match_cond
import json
sql = webnotes.conn.sql
class NotUpdateStockError(webnotes.ValidationError): pass
class StockOverReturnError(webnotes.ValidationError): pass
class IncorrectValuationRateError(webnotes.ValidationError): pass
class DuplicateEntryForProductionOrderError(webnotes.ValidationError): pass
class StockOverProductionError(webnotes.ValidationError): pass
from controllers.stock_controller import StockController
@@ -52,14 +52,15 @@ class DocType(StockController):
def on_submit(self):
self.update_stock_ledger()
self.update_serial_no(1)
self.update_production_order(1)
from stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
update_serial_nos_after_submit(self, "mtn_details")
self.update_production_order()
self.make_gl_entries()
def on_cancel(self):
self.update_stock_ledger()
self.update_serial_no(0)
self.update_production_order(0)
self.update_production_order()
self.make_cancel_gl_entries()
def validate_fiscal_year(self):
@@ -294,24 +295,6 @@ class DocType(StockController):
from `tabStock Entry Detail` where parent in (
select name from `tabStock Entry` where `%s`=%s and docstatus=1)
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):
sl_entries = []
@@ -342,42 +325,49 @@ class DocType(StockController):
self.make_sl_entries(sl_entries, self.doc.amended_from and 'Yes' or 'No')
def update_production_order(self, is_submit):
def update_production_order(self):
def _validate_production_order(pro_bean):
if flt(pro_bean.doc.docstatus) != 1:
webnotes.throw(_("Production Order must be submitted") + ": " +
self.doc.production_order)
if pro_bean.doc.status == 'Stopped':
msgprint(_("Transaction not allowed against stopped Production Order") + ": " +
self.doc.production_order)
if self.doc.production_order:
# first perform some validations
# (they are here coz this fn is also called during on_cancel)
pro_obj = get_obj("Production Order", self.doc.production_order)
if flt(pro_obj.doc.docstatus) != 1:
msgprint("""You cannot do any transaction against
Production Order : %s, as it's not submitted"""
% (pro_obj.doc.name), raise_exception=1)
if pro_obj.doc.status == 'Stopped':
msgprint("""You cannot do any transaction against Production Order : %s,
as it's status is 'Stopped'"""% (pro_obj.doc.name), raise_exception=1)
# update bin
if self.doc.purpose == "Manufacture/Repack":
from stock.utils import update_bin
pro_obj.doc.produced_qty = flt(pro_obj.doc.produced_qty) + \
(is_submit and 1 or -1 ) * flt(self.doc.fg_completed_qty)
args = {
"item_code": pro_obj.doc.production_item,
"warehouse": pro_obj.doc.fg_warehouse,
"posting_date": self.doc.posting_date,
"planned_qty": (is_submit and -1 or 1 ) * flt(self.doc.fg_completed_qty)
}
update_bin(args)
pro_bean = webnotes.bean("Production Order", self.doc.production_order)
_validate_production_order(pro_bean)
self.update_produced_qty(pro_bean)
self.update_planned_qty(pro_bean)
# update production order status
pro_obj.doc.status = (flt(pro_obj.doc.qty)==flt(pro_obj.doc.produced_qty)) \
and 'Completed' or 'In Process'
pro_obj.doc.save()
def update_produced_qty(self, pro_bean):
if self.doc.purpose == "Manufacture/Repack":
produced_qty = flt(pro_bean.doc.produced_qty) + \
(self.doc.docstatus==1 and 1 or -1 ) * flt(self.doc.fg_completed_qty)
if produced_qty > flt(pro_bean.doc.qty):
webnotes.throw(_("Production Order") + ": " + self.doc.production_order + "\n" +
_("Total Manufactured Qty can not be greater than Planned qty to manufacture")
+ "(%s/%s)" % (produced_qty, flt(pro_bean.doc.qty)), StockOverProductionError)
status = 'Completed' if flt(produced_qty) >= flt(pro_bean.doc.qty) else 'In Process'
webnotes.conn.sql("""update `tabProduction Order` set status=%s, produced_qty=%s
where name=%s""", (status, produced_qty, self.doc.production_order))
def update_planned_qty(self, pro_bean):
from stock.utils import update_bin
update_bin({
"item_code": pro_bean.doc.production_item,
"warehouse": pro_bean.doc.fg_warehouse,
"posting_date": self.doc.posting_date,
"planned_qty": (self.doc.docstatus==1 and -1 or 1 ) * flt(self.doc.fg_completed_qty)
})
def get_item_details(self, arg):
arg = json.loads(arg)
item = sql("""select stock_uom, description, item_name from `tabItem`
item = webnotes.conn.sql("""select stock_uom, description, item_name from `tabItem`
where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00'
or end_of_life > now())""", (arg.get('item_code')), as_dict = 1)
if not item:
@@ -401,7 +391,7 @@ class DocType(StockController):
def get_uom_details(self, arg = ''):
arg, ret = eval(arg), {}
uom = sql("""select conversion_factor from `tabUOM Conversion Detail`
uom = webnotes.conn.sql("""select conversion_factor from `tabUOM Conversion Detail`
where parent = %s and uom = %s""", (arg['item_code'], arg['uom']), as_dict = 1)
if not uom or not flt(uom[0].conversion_factor):
msgprint("There is no Conversion Factor for UOM '%s' in Item '%s'" % (arg['uom'],
@@ -431,7 +421,8 @@ class DocType(StockController):
return ret
def get_items(self):
self.doclist = self.doc.clear_table(self.doclist, 'mtn_details', 1)
self.doclist = filter(lambda d: d.parentfield!="mtn_details", self.doclist)
# self.doclist = self.doc.clear_table(self.doclist, 'mtn_details')
pro_obj = None
if self.doc.production_order:
@@ -470,7 +461,7 @@ class DocType(StockController):
"stock_uom": pro_obj.doc.stock_uom
}
}, bom_no=pro_obj.doc.bom_no)
elif self.doc.purpose in ["Material Receipt", "Manufacture/Repack"]:
if self.doc.purpose=="Material Receipt":
self.doc.from_warehouse = ""
@@ -487,70 +478,19 @@ class DocType(StockController):
}, bom_no=self.doc.bom_no)
self.get_stock_and_rate()
def get_bom_raw_materials(self, qty):
"""
get all items from flat bom except
child items of sub-contracted and sub assembly items
and sub assembly items itself.
"""
from manufacturing.doctype.bom.bom import get_bom_items_as_dict
# item dict = { item_code: {qty, description, stock_uom} }
item_dict = {}
item_dict = get_bom_items_as_dict(self.doc.bom_no, qty=qty, fetch_exploded = self.doc.use_multi_level_bom)
def _make_items_dict(items_list):
"""makes dict of unique items with it's qty"""
for item in items_list:
if item_dict.has_key(item.item_code):
item_dict[item.item_code]["qty"] += flt(item.qty)
else:
item_dict[item.item_code] = {
"qty": flt(item.qty),
"description": item.description,
"stock_uom": item.stock_uom,
"from_warehouse": item.default_warehouse
}
if self.doc.use_multi_level_bom:
# get all raw materials with sub assembly childs
fl_bom_sa_child_item = sql("""select
fb.item_code,
ifnull(sum(fb.qty_consumed_per_unit),0)*%s as qty,
fb.description,
fb.stock_uom,
it.default_warehouse
from
`tabBOM Explosion Item` fb,`tabItem` it
where
it.name = fb.item_code
and ifnull(it.is_pro_applicable, 'No') = 'No'
and ifnull(it.is_sub_contracted_item, 'No') = 'No'
and fb.docstatus < 2
and fb.parent=%s group by item_code, stock_uom""",
(qty, self.doc.bom_no), as_dict=1)
if fl_bom_sa_child_item:
_make_items_dict(fl_bom_sa_child_item)
else:
# get only BOM items
fl_bom_sa_items = sql("""select
`tabItem`.item_code,
ifnull(sum(`tabBOM Item`.qty_consumed_per_unit), 0) *%s as qty,
`tabItem`.description,
`tabItem`.stock_uom,
`tabItem`.default_warehouse
from
`tabBOM Item`, `tabItem`
where
`tabBOM Item`.parent = %s and
`tabBOM Item`.item_code = tabItem.name and
`tabBOM Item`.docstatus < 2
group by item_code""", (qty, self.doc.bom_no), as_dict=1)
if fl_bom_sa_items:
_make_items_dict(fl_bom_sa_items)
for item in item_dict.values():
item.from_warehouse = item.default_warehouse
return item_dict
def get_pending_raw_materials(self, pro_obj):
"""
issue (item quantity) that is pending to issue or desire to transfer,
@@ -590,7 +530,7 @@ class DocType(StockController):
def get_issued_qty(self):
issued_item_qty = {}
result = sql("""select t1.item_code, sum(t1.qty)
result = webnotes.conn.sql("""select t1.item_code, sum(t1.qty)
from `tabStock Entry Detail` t1, `tabStock Entry` t2
where t1.parent = t2.name and t2.production_order = %s and t2.docstatus = 1
and t2.purpose = 'Material Transfer'
@@ -636,7 +576,7 @@ class DocType(StockController):
def get_cust_addr(self):
from utilities.transaction_base import get_default_address, get_address_display
res = sql("select customer_name from `tabCustomer` where name = '%s'"%self.doc.customer)
res = webnotes.conn.sql("select customer_name from `tabCustomer` where name = '%s'"%self.doc.customer)
address_display = None
customer_address = get_default_address("customer", self.doc.customer)
if customer_address:
@@ -657,7 +597,7 @@ class DocType(StockController):
def get_supp_addr(self):
from utilities.transaction_base import get_default_address, get_address_display
res = sql("""select supplier_name from `tabSupplier`
res = webnotes.conn.sql("""select supplier_name from `tabSupplier`
where name=%s""", self.doc.supplier)
address_display = None
supplier_address = get_default_address("customer", self.doc.customer)
@@ -896,8 +836,7 @@ def make_return_jv_from_delivery_note(se, ref):
ref.doclist[0].name)
if not invoices_against_delivery:
sales_orders_against_delivery = [d.prevdoc_docname for d in
ref.doclist.get({"prevdoc_doctype": "Sales Order"}) if d.prevdoc_docname]
sales_orders_against_delivery = [d.against_sales_order for d in ref.doclist if d.against_sales_order]
if sales_orders_against_delivery:
invoices_against_delivery = get_invoice_list("Sales Invoice Item", "sales_order",

View File

@@ -1,13 +1,10 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
# License: GNU General Public License v3. See license.txt
# ERPNext - web based ERP (http://erpnext.com)
# For license information, please see license.txt
from __future__ import unicode_literals
import webnotes, unittest
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
@@ -43,12 +40,13 @@ class TestStockEntry(unittest.TestCase):
webnotes.conn.set_default("company", self.old_default_company)
def test_warehouse_company_validation(self):
set_perpetual_inventory(0)
self._clear_stock_account_balance()
webnotes.session.user = "test2@example.com"
webnotes.bean("Profile", "test2@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
webnotes.session.user = "test2@example.com"
from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany
from stock.utils import InvalidWarehouseCompany
st1 = webnotes.bean(copy=test_records[0])
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
st1.insert()
@@ -57,15 +55,15 @@ class TestStockEntry(unittest.TestCase):
webnotes.session.user = "Administrator"
def test_warehouse_user(self):
set_perpetual_inventory(0)
from stock.utils import UserNotAllowedForWarehouse
webnotes.session.user = "test@example.com"
webnotes.bean("Profile", "test@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
webnotes.bean("Profile", "test2@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
webnotes.session.user = "test@example.com"
st1 = webnotes.bean(copy=test_records[0])
st1.doc.company = "_Test Company 1"
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
@@ -721,6 +719,18 @@ class TestStockEntry(unittest.TestCase):
se.insert()
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):
self._clear_stock_account_balance()
se = make_serialized_item()

View File

@@ -0,0 +1,57 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
from webnotes.utils import add_days, cstr, flt, nowdate, cint, now
from webnotes.model.doc import Document
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import session, msgprint
from stock.utils import get_valid_serial_nos
class DocType:
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
def update_stock(self, values, is_amended = 'No'):
for v in values:
sle_id = ''
# reverse quantities for cancel
if v.get('is_cancelled') == 'Yes':
v['actual_qty'] = -flt(v['actual_qty'])
# cancel matching entry
webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
modified=%s, modified_by=%s
where voucher_no=%s and voucher_type=%s""",
(now(), webnotes.session.user, v['voucher_no'], v['voucher_type']))
if v.get("actual_qty"):
sle_id = self.make_entry(v)
args = v.copy()
args.update({
"sle_id": sle_id,
"is_amended": is_amended
})
get_obj('Warehouse', v["warehouse"]).update_bin(args)
def make_entry(self, args):
args.update({"doctype": "Stock Ledger Entry"})
sle = webnotes.bean([args])
sle.ignore_permissions = 1
sle.insert()
return sle.doc.name
def repost(self):
"""
Repost everything!
"""
for wh in webnotes.conn.sql("select name from tabWarehouse"):
get_obj('Warehouse', wh[0]).repost_stock()

View File

@@ -3,37 +3,21 @@
from __future__ import unicode_literals
import webnotes
from webnotes import _, msgprint, ValidationError
from webnotes.utils import cint, flt, getdate, cstr
from webnotes import msgprint
from webnotes.utils import flt, getdate
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):
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
def validate(self):
from stock.utils import validate_warehouse_user
if not hasattr(webnotes, "new_stock_ledger_entries"):
webnotes.new_stock_ledger_entries = []
webnotes.new_stock_ledger_entries.append(self.doc)
from stock.utils import validate_warehouse_user, validate_warehouse_company
self.validate_mandatory()
self.validate_item()
validate_warehouse_user(self.doc.warehouse)
self.validate_warehouse_company()
validate_warehouse_company(self.doc.warehouse, self.doc.company)
self.scrub_posting_time()
from accounts.utils import validate_fiscal_year
@@ -42,7 +26,9 @@ class DocType(DocListController):
def on_submit(self):
self.check_stock_frozen_date()
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
def actual_amt_check(self):
@@ -62,14 +48,7 @@ class DocType(DocListController):
<b>%(item_code)s</b> at Warehouse <b>%(warehouse)s</b> \
as on %(posting_date)s %(posting_time)s""" % self.doc.fields)
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)
self.doc.fields.pop('batch_bal')
def validate_mandatory(self):
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
@@ -81,7 +60,10 @@ class DocType(DocListController):
msgprint("Warehouse: '%s' does not exist in the system. Please check." % self.doc.fields.get(k), raise_exception = 1)
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':
webnotes.throw("""Item: "%s" is not a Stock Item.""" % self.doc.item_code)
@@ -99,95 +81,6 @@ class DocType(DocListController):
if not self.doc.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),
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):
stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
if stock_frozen_upto:
@@ -199,21 +92,6 @@ class DocType(DocListController):
if not self.doc.posting_time or self.doc.posting_time == '00:0':
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():
if not webnotes.conn.sql("""show index from `tabStock Ledger Entry`
where Key_name="posting_sort_index" """):

View File

@@ -246,6 +246,7 @@ class DocType(StockController):
"stock_uom": webnotes.conn.get_value("Item", row.item_code, "stock_uom"),
"voucher_detail_no": row.voucher_detail_no,
"fiscal_year": self.doc.fiscal_year,
"is_cancelled": "No"
})
args.update(opts)
self.make_sl_entries([args])

View File

@@ -10,7 +10,6 @@ from webnotes.model.bean import copy_doclist
from webnotes.model.code import get_obj
from webnotes import msgprint, _
sql = webnotes.conn.sql
class DocType:
def __init__(self, d, dl=[]):
@@ -34,7 +33,7 @@ class DocType:
msgprint("Please Enter Conversion Factor.")
raise Exception
stock_uom = sql("select stock_uom from `tabItem` where name = '%s'" % self.doc.item_code)
stock_uom = webnotes.conn.sql("select stock_uom from `tabItem` where name = '%s'" % self.doc.item_code)
stock_uom = stock_uom and stock_uom[0][0]
if cstr(self.doc.new_stock_uom) == cstr(stock_uom):
msgprint("Item Master is already updated with New Stock UOM " + cstr(self.doc.new_stock_uom))
@@ -50,9 +49,9 @@ class DocType:
def update_bin(self):
# update bin
if flt(self.doc.conversion_factor) != flt(1):
sql("update `tabBin` set stock_uom = '%s' , indented_qty = ifnull(indented_qty,0) * %s, ordered_qty = ifnull(ordered_qty,0) * %s, reserved_qty = ifnull(reserved_qty,0) * %s, planned_qty = ifnull(planned_qty,0) * %s, projected_qty = actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.item_code) )
webnotes.conn.sql("update `tabBin` set stock_uom = '%s' , indented_qty = ifnull(indented_qty,0) * %s, ordered_qty = ifnull(ordered_qty,0) * %s, reserved_qty = ifnull(reserved_qty,0) * %s, planned_qty = ifnull(planned_qty,0) * %s, projected_qty = actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.item_code) )
else:
sql("update `tabBin` set stock_uom = '%s' where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.item_code) )
webnotes.conn.sql("update `tabBin` set stock_uom = '%s' where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.item_code) )
# acknowledge user
msgprint(" All Bins Updated Successfully.")
@@ -62,16 +61,16 @@ class DocType:
from stock.stock_ledger import update_entries_after
if flt(self.doc.conversion_factor) != flt(1):
sql("update `tabStock Ledger Entry` set stock_uom = '%s', actual_qty = ifnull(actual_qty,0) * '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.item_code))
webnotes.conn.sql("update `tabStock Ledger Entry` set stock_uom = '%s', actual_qty = ifnull(actual_qty,0) * '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.item_code))
else:
sql("update `tabStock Ledger Entry` set stock_uom = '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.item_code))
webnotes.conn.sql("update `tabStock Ledger Entry` set stock_uom = '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.item_code))
# acknowledge user
msgprint("Stock Ledger Entries Updated Successfully.")
# update item valuation
if flt(self.doc.conversion_factor) != flt(1):
wh = sql("select name from `tabWarehouse`")
wh = webnotes.conn.sql("select name from `tabWarehouse`")
for w in wh:
update_entries_after({"item_code": self.doc.item_code, "warehouse": w[0]})

View File

@@ -5,10 +5,8 @@ from __future__ import unicode_literals
import webnotes
from webnotes.utils import cint, flt, validate_email_add
from webnotes.model.code import get_obj
from webnotes import msgprint, _
sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=[]):
@@ -32,7 +30,7 @@ class DocType:
if not webnotes.conn.get_value("Account", {"account_type": "Warehouse",
"master_name": self.doc.name}) and not webnotes.conn.get_value("Account",
{"account_name": self.doc.warehouse_name}):
if self.doc.__islocal or not webnotes.conn.get_value("Stock Ledger Entry",
if self.doc.fields.get("__islocal") or not webnotes.conn.get_value("Stock Ledger Entry",
{"warehouse": self.doc.name}):
self.validate_parent_account()
ac_bean = webnotes.bean({
@@ -59,7 +57,16 @@ class DocType:
else:
webnotes.throw(_("Please enter account group under which account \
for warehouse ") + self.doc.name +_(" will be created"))
def on_rename(self, new, old, merge=False):
webnotes.conn.set_value("Account", {"account_type": "Warehouse", "master_name": old},
"master_name", new)
if merge:
from stock.stock_ledger import update_entries_after
for item_code in webnotes.conn.sql("""select item_code from `tabBin`
where warehouse=%s""", new):
update_entries_after({"item_code": item_code, "warehouse": new})
def merge_warehouses(self):
webnotes.conn.auto_commit_on_many_writes = 1
@@ -83,113 +90,25 @@ class DocType:
webnotes.conn.delete_doc("Account", old_warehouse_account)
from utilities.repost_stock import repost
for item_code in items:
self.repost(item_code[0], self.doc.merge_with)
repost(item_code[0], self.doc.merge_with)
webnotes.conn.auto_commit_on_many_writes = 0
msgprint("Warehouse %s merged into %s. Now you can delete warehouse: %s"
% (self.doc.name, self.doc.merge_with, self.doc.name))
def repost(self, item_code, warehouse=None):
from stock.utils import get_bin
self.repost_actual_qty(item_code, warehouse)
bin = get_bin(item_code, warehouse)
self.repost_reserved_qty(bin)
self.repost_indented_qty(bin)
self.repost_ordered_qty(bin)
self.repost_planned_qty(bin)
bin.doc.projected_qty = flt(bin.doc.actual_qty) + flt(bin.doc.planned_qty) \
+ flt(bin.doc.indented_qty) + flt(bin.doc.ordered_qty) - flt(bin.doc.reserved_qty)
bin.doc.save()
def repost_actual_qty(self, item_code, warehouse=None):
from stock.stock_ledger import update_entries_after
if not warehouse:
warehouse = self.doc.name
update_entries_after({ "item_code": item_code, "warehouse": warehouse })
def repost_reserved_qty(self, bin):
reserved_qty = webnotes.conn.sql("""
select
sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty))
from
(
select
qty as dnpi_qty,
(
select qty from `tabSales Order Item`
where name = dnpi.parent_detail_docname
) as so_item_qty,
(
select ifnull(delivered_qty, 0) from `tabSales Order Item`
where name = dnpi.parent_detail_docname
) as so_item_delivered_qty
from
(
select qty, parent_detail_docname
from `tabDelivery Note Packing Item` dnpi_in
where item_code = %s and warehouse = %s
and parenttype="Sales Order"
and exists (select * from `tabSales Order` so
where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
) dnpi
) tab
where
so_item_qty >= so_item_delivered_qty
""", (bin.doc.item_code, bin.doc.warehouse))
if flt(bin.doc.reserved_qty) != flt(reserved_qty[0][0]):
webnotes.conn.set_value("Bin", bin.doc.name, "reserved_qty", flt(reserved_qty[0][0]))
def repost_indented_qty(self, bin):
indented_qty = webnotes.conn.sql("""select sum(pr_item.qty - pr_item.ordered_qty)
from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr
where pr_item.item_code=%s and pr_item.warehouse=%s
and pr_item.qty > pr_item.ordered_qty and pr_item.parent=pr.name
and pr.status!='Stopped' and pr.docstatus=1"""
, (bin.doc.item_code, bin.doc.warehouse))
if flt(bin.doc.indented_qty) != flt(indented_qty[0][0]):
webnotes.conn.set_value("Bin", bin.doc.name, "indented_qty", flt(indented_qty[0][0]))
def repost_ordered_qty(self, bin):
ordered_qty = webnotes.conn.sql("""
select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor)
from `tabPurchase Order Item` po_item, `tabPurchase Order` po
where po_item.item_code=%s and po_item.warehouse=%s
and po_item.qty > po_item.received_qty and po_item.parent=po.name
and po.status!='Stopped' and po.docstatus=1"""
, (bin.doc.item_code, bin.doc.warehouse))
if flt(bin.doc.ordered_qty) != flt(ordered_qty[0][0]):
webnotes.conn.set_value("Bin", bin.doc.name, "ordered_qty", flt(ordered_qty[0][0]))
def repost_planned_qty(self, bin):
planned_qty = webnotes.conn.sql("""
select sum(qty - produced_qty) from `tabProduction Order`
where production_item = %s and fg_warehouse = %s and status != "Stopped"
and docstatus=1""", (bin.doc.item_code, bin.doc.warehouse))
if flt(bin.doc.planned_qty) != flt(planned_qty[0][0]):
webnotes.conn.set_value("Bin", bin.doc.name, "planned_qty", flt(planned_qty[0][0]))
def on_trash(self):
# delete bin
bins = sql("select * from `tabBin` where warehouse = %s", self.doc.name, as_dict=1)
bins = webnotes.conn.sql("select * from `tabBin` where warehouse = %s", self.doc.name, as_dict=1)
for d in bins:
if d['actual_qty'] or d['reserved_qty'] or d['ordered_qty'] or \
d['indented_qty'] or d['projected_qty'] or d['planned_qty']:
msgprint("""Warehouse: %s can not be deleted as qty exists for item: %s"""
% (self.doc.name, d['item_code']), raise_exception=1)
else:
sql("delete from `tabBin` where name = %s", d['name'])
webnotes.conn.sql("delete from `tabBin` where name = %s", d['name'])
warehouse_account = webnotes.conn.get_value("Account",
{"account_type": "Warehosue", "master_name": self.doc.name})
@@ -197,18 +116,8 @@ class DocType:
webnotes.delete_doc("Account", warehouse_account)
# delete cancelled sle
if sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name):
if webnotes.conn.sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name):
msgprint("""Warehosue can not be deleted as stock ledger entry
exists for this warehouse.""", raise_exception=1)
else:
sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name)
def on_rename(self, newdn, olddn, merge=False):
webnotes.conn.set_value("Account", {"account_type": "Warehouse", "master_name": olddn},
"master_name", newdn)
if merge:
from stock.stock_ledger import update_entries_after
for item_code in webnotes.conn.sql("""select item_code from `tabBin`
where warehouse=%s""", newdn):
update_entries_after({"item_code": item_code, "warehouse": newdn})
webnotes.conn.sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name)

View File

@@ -126,10 +126,11 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
} else {
item.inflow_value += value_diff;
}
}
item.closing_qty += qty_diff;
item.closing_value += value_diff;
item.closing_qty += qty_diff;
item.closing_value += value_diff;
}
} else {
break;
}

View File

@@ -10,6 +10,9 @@ import json
# future reposting
class NegativeStockError(webnotes.ValidationError): pass
_exceptions = webnotes.local('stockledger_exceptions')
# _exceptions = []
def make_sl_entries(sl_entries, is_amended=None):
if sl_entries:
from stock.utils import update_bin
@@ -55,7 +58,6 @@ def delete_cancelled_entry(voucher_type, voucher_no):
webnotes.conn.sql("""delete from `tabStock Ledger Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
_exceptions = []
def update_entries_after(args, verbose=1):
"""
update valution rate and qty after transaction
@@ -68,8 +70,8 @@ def update_entries_after(args, verbose=1):
"posting_time": "12:00"
}
"""
global _exceptions
_exceptions = []
if not _exceptions:
webnotes.local.stockledger_exceptions = []
previous_sle = get_sle_before_datetime(args)
@@ -190,10 +192,12 @@ def validate_negative_stock(qty_after_transaction, sle):
will not consider cancelled entries
"""
diff = qty_after_transaction + flt(sle.actual_qty)
if not _exceptions:
webnotes.local.stockledger_exceptions = []
if diff < 0 and abs(diff) > 0.0001:
# negative stock!
global _exceptions
exc = sle.copy().update({"diff": diff})
_exceptions.append(exc)
return False
@@ -327,4 +331,4 @@ def get_previous_sle(args, for_update=False):
sle = get_stock_ledger_entries(args, ["name != %(sle)s",
"timestamp(posting_date, posting_time) <= timestamp(%(posting_date)s, %(posting_time)s)"],
"desc", "limit 1", for_update=for_update)
return sle and sle[0] or {}
return sle and sle[0] or {}

View File

@@ -9,6 +9,7 @@ from webnotes.defaults import get_global_default
from webnotes.utils.email_lib import sendmail
class UserNotAllowedForWarehouse(webnotes.ValidationError): pass
class InvalidWarehouseCompany(webnotes.ValidationError): pass
def get_stock_balance_on(warehouse, posting_date=None):
if not posting_date: posting_date = nowdate()
@@ -208,7 +209,7 @@ def get_warehouse_list(doctype, txt, searchfield, start, page_len, filters):
return wlist
def validate_warehouse_user(warehouse):
if webnotes.session.user=="Administrator":
if webnotes.session.user=="Administrator" or not warehouse:
return
warehouse_users = [p[0] for p in webnotes.conn.sql("""select user from `tabWarehouse User`
where parent=%s""", warehouse)]
@@ -216,6 +217,12 @@ def validate_warehouse_user(warehouse):
if warehouse_users and not (webnotes.session.user in warehouse_users):
webnotes.throw(_("Not allowed entry in Warehouse") \
+ ": " + 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,
stock_ledger_entries, item_sales_bom):
@@ -245,10 +252,10 @@ def get_buying_amount(voucher_type, voucher_no, item_row, stock_ledger_entries):
def reorder_item():
""" Reorder item if stock reaches reorder level"""
if not hasattr(webnotes, "auto_indent"):
webnotes.auto_indent = cint(webnotes.conn.get_value('Stock Settings', None, 'auto_indent'))
if getattr(webnotes.local, "auto_indent", None) is None:
webnotes.local.auto_indent = cint(webnotes.conn.get_value('Stock Settings', None, 'auto_indent'))
if webnotes.auto_indent:
if webnotes.local.auto_indent:
material_requests = {}
bin_list = webnotes.conn.sql("""select item_code, warehouse, projected_qty
from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''
@@ -333,18 +340,18 @@ def create_material_request(material_requests):
mr_list.append(mr_bean)
except:
if webnotes.message_log:
exceptions_list.append([] + webnotes.message_log)
webnotes.message_log = []
if webnotes.local.message_log:
exceptions_list.append([] + webnotes.local.message_log)
webnotes.local.message_log = []
else:
exceptions_list.append(webnotes.getTraceback())
if mr_list:
if not hasattr(webnotes, "reorder_email_notify"):
webnotes.reorder_email_notify = cint(webnotes.conn.get_value('Stock Settings', None,
if getattr(webnotes.local, "reorder_email_notify", None) is None:
webnotes.local.reorder_email_notify = cint(webnotes.conn.get_value('Stock Settings', None,
'reorder_email_notify'))
if(webnotes.reorder_email_notify):
if(webnotes.local.reorder_email_notify):
send_email_notification(mr_list)
if exceptions_list:
@@ -387,12 +394,3 @@ def notify_errors(exceptions_list):
from webnotes.profile import get_system_managers
sendmail(get_system_managers(), subject=subject, msg=msg)
def repost():
"""
Repost everything!
"""
from webnotes.model.code import get_obj
for wh in webnotes.conn.sql("select name from tabWarehouse"):
get_obj('Warehouse', wh[0]).repost_stock()