mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 16:34:46 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}],
|
||||
]
|
||||
@@ -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")
|
||||
@@ -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();
|
||||
},
|
||||
|
||||
@@ -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'"""
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"))
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
57
stock/doctype/stock_ledger/stock_ledger.py
Normal file
57
stock/doctype/stock_ledger/stock_ledger.py
Normal 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()
|
||||
@@ -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" """):
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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]})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user