Merge branch 'master' into perpetual
diff --git a/accounts/doctype/accounts_settings/accounts_settings.py b/accounts/doctype/accounts_settings/accounts_settings.py
index b548936..ed7b402 100644
--- a/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/accounts/doctype/accounts_settings/accounts_settings.py
@@ -2,23 +2,24 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint
+from webnotes.utils import cint, cstr
+from webnotes import msgprint, _
class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
def validate(self):
- self.make_adjustment_jv_for_auto_inventory()
+ self.validate_perpetual_accounting()
- def make_adjustment_jv_for_auto_inventory(self):
- previous_auto_inventory_accounting = cint(webnotes.conn.get_value("Accounts Settings",
- None, "auto_inventory_accounting"))
- if cint(self.doc.auto_inventory_accounting) != previous_auto_inventory_accounting:
- from accounts.utils import create_stock_in_hand_jv
- create_stock_in_hand_jv(reverse = \
- cint(self.doc.auto_inventory_accounting) < previous_auto_inventory_accounting)
-
+ def validate_perpetual_accounting(self):
+ if cint(self.doc.perpetual_accounting) == 1:
+ previous_val = cint(webnotes.conn.get_value("Accounts Settings",
+ None, "perpetual_accounting"))
+ if cint(self.doc.perpetual_accounting) != previous_val:
+ from accounts.utils import validate_stock_and_account_balance
+ validate_stock_and_account_balance()
+
def on_update(self):
- for key in ["auto_inventory_accounting"]:
+ for key in ["perpetual_accounting"]:
webnotes.conn.set_default(key, self.doc.fields.get(key, ''))
diff --git a/accounts/doctype/accounts_settings/accounts_settings.txt b/accounts/doctype/accounts_settings/accounts_settings.txt
index b8be161..b7ab69e 100644
--- a/accounts/doctype/accounts_settings/accounts_settings.txt
+++ b/accounts/doctype/accounts_settings/accounts_settings.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-06-24 15:49:57",
"docstatus": 0,
- "modified": "2013-07-05 14:23:40",
+ "modified": "2013-08-01 17:35:16",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -39,11 +39,12 @@
"name": "Accounts Settings"
},
{
+ "default": "1",
"description": "If enabled, the system will post accounting entries for inventory automatically.",
"doctype": "DocField",
- "fieldname": "auto_inventory_accounting",
+ "fieldname": "perpetual_accounting",
"fieldtype": "Check",
- "label": "Enable Auto Inventory Accounting"
+ "label": "Enable Perpetual Accounting for Inventory"
},
{
"description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 09df5f6..a33f6b9 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -105,7 +105,7 @@
sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
- self.update_stock_ledger(update_stock=1)
+ self.update_stock_ledger()
else:
# Check for Approving Authority
if not self.doc.recurring_id:
@@ -137,7 +137,7 @@
sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
- self.update_stock_ledger(update_stock = -1)
+ self.update_stock_ledger()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
@@ -549,45 +549,19 @@
submitted = webnotes.conn.sql("select name from `tabDelivery Note` where docstatus = 1 and name = '%s'" % d.delivery_note)
if not submitted:
msgprint("Delivery Note : "+ cstr(d.delivery_note) +" is not submitted")
- raise Exception , "Validation Error."
+ raise Exception , "Validation Error."
-
- def make_sl_entry(self, d, wh, qty, in_value, update_stock):
- st_uom = webnotes.conn.sql("select stock_uom from `tabItem` where name = '%s'"%d['item_code'])
- self.values.append({
- 'item_code' : d['item_code'],
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Sales Invoice',
- 'voucher_no' : cstr(self.doc.name),
- 'voucher_detail_no' : cstr(d['name']),
- 'actual_qty' : qty,
- 'stock_uom' : st_uom and st_uom[0][0] or '',
- 'incoming_rate' : in_value,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : (update_stock==1) and 'No' or 'Yes',
- 'batch_no' : cstr(d['batch_no']),
- 'serial_no' : d['serial_no'],
- "project" : self.doc.project_name
- })
-
- def update_stock_ledger(self, update_stock):
- self.values = []
+ def update_stock_ledger(self):
+ sl_entries = []
items = get_obj('Sales Common').get_item_list(self)
for d in items:
- stock_item = webnotes.conn.sql("SELECT is_stock_item, is_sample_item \
- FROM tabItem where name = '%s'"%(d['item_code']), as_dict = 1)
- if stock_item[0]['is_stock_item'] == "Yes":
- if not d['warehouse']:
- msgprint("Message: Please enter Warehouse for item %s as it is stock item." \
- % d['item_code'], raise_exception=1)
-
- # Reduce actual qty from warehouse
- self.make_sl_entry( d, d['warehouse'], - flt(d['qty']) , 0, update_stock)
-
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
+ if d.item_code in self.stock_items and d.warehouse:
+ sl_entries.append(self.get_sl_entries(d, {
+ "actual_qty": -1*flt(d.qty),
+ "stock_uom": webnotes.conn.get_value("Item", d.item_code, "stock_uom")
+ }))
+
+ self.make_sl_entries(sl_entries)
def make_gl_entries(self):
from accounts.general_ledger import make_gl_entries, merge_similar_entries
diff --git a/accounts/utils.py b/accounts/utils.py
index 77665ea..690371e 100644
--- a/accounts/utils.py
+++ b/accounts/utils.py
@@ -17,7 +17,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import nowdate, cstr, flt, now
+from webnotes.utils import nowdate, nowtime, cstr, flt, now
from webnotes.model.doc import addchild
from webnotes import msgprint, _
from webnotes.utils import formatdate
@@ -351,4 +351,42 @@
webnotes.conn.sql("""update `tabGL Entry` set %s = %s + %s
where voucher_type = %s and voucher_no = %s and %s > 0 limit 1""" %
(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),
- (d.diff, d.voucher_type, d.voucher_no))
\ No newline at end of file
+ (d.diff, d.voucher_type, d.voucher_no))
+
+def validate_stock_and_account_balance():
+ difference = get_stock_and_account_difference()
+ if difference:
+ msgprint(_("Account balance must be synced with stock balance, \
+ to enable perpetual accounting." +
+ _(" Following accounts are not synced with stock balance") + ": \n" +
+ "\n".join(difference.keys())), raise_exception=1)
+
+def get_stock_and_account_difference(warehouse_list=None):
+ from stock.utils import get_latest_stock_balance
+
+ if not warehouse_list:
+ warehouse_list = webnotes.conn.sql_list("""select name from tabWarehouse
+ where docstatus<2""")
+
+ account_warehouse_map = {}
+ warehouse_with_no_account = []
+ difference = {}
+
+ warehouse_account = webnotes.conn.sql("""select name, account from tabWarehouse
+ where name in (%s)""" % ', '.join(['%s']*len(warehouse_list)), warehouse_list, as_dict=1)
+
+ for wh in warehouse_account:
+ if not wh.account: warehouse_with_no_account.append(wh.name)
+ account_warehouse_map.setdefault(wh.account, []).append(wh.name)
+
+ if warehouse_with_no_account:
+ msgprint(_("Please mention Perpetual Account in warehouse master for following warehouses")
+ + ": " + '\n'.join(warehouse_with_no_account), raise_exception=1)
+
+ for account, warehouse in account_warehouse_map.items():
+ account_balance = get_balance_on(account)
+ stock_value = get_latest_stock_balance(warehouse)
+
+ difference.setdefault(account, (account_balance - stock_value))
+
+ return difference
\ No newline at end of file
diff --git a/controllers/stock_controller.py b/controllers/stock_controller.py
index 7cfb68c..03bdc98 100644
--- a/controllers/stock_controller.py
+++ b/controllers/stock_controller.py
@@ -16,7 +16,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint
+from webnotes.utils import cint, flt, cstr
import webnotes.defaults
from controllers.accounts_controller import AccountsController
@@ -49,6 +49,35 @@
]
return gl_entries
+
+
+ def get_sl_entries(self, d, args):
+ sl_dict = {
+ "item_code": d.item_code,
+ "warehouse": d.warehouse,
+ "posting_date": self.doc.posting_date,
+ "posting_time": self.doc.posting_time,
+ "voucher_type": self.doc.doctype,
+ "voucher_no": self.doc.name,
+ "voucher_detail_no": d.name,
+ "actual_qty": (self.doc.docstatus==1 and 1 or -1)*flt(d.stock_qty),
+ "stock_uom": d.stock_uom,
+ "incoming_rate": 0,
+ "company": self.doc.company,
+ "fiscal_year": self.doc.fiscal_year,
+ "is_cancelled": self.doc.docstatus==2 and "Yes" or "No",
+ "batch_no": cstr(d.batch_no).strip(),
+ "serial_no": d.serial_no,
+ "project": d.project_name
+ }
+
+ sl_dict.update(args)
+ return sl_dict
+
+ def make_sl_entries(self, sl_entries, is_amended=None):
+ if sl_entries:
+ from webnotes.model.code import get_obj
+ get_obj('Stock Ledger').update_stock(sl_entries, is_amended)
def get_stock_ledger_entries(self, item_list=None, warehouse_list=None):
if not (item_list and warehouse_list):
diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py
index c174b13..ef14054 100644
--- a/selling/doctype/sales_common/sales_common.py
+++ b/selling/doctype/sales_common/sales_common.py
@@ -149,7 +149,7 @@
for p in getlist(obj.doclist, 'packing_details'):
if p.parent_detail_docname == d.name and p.parent_item == d.item_code:
# the packing details table's qty is already multiplied with parent's qty
- il.append({
+ il.append(webnotes._dict({
'warehouse': p.warehouse,
'reserved_warehouse': reserved_warehouse,
'item_code': p.item_code,
@@ -159,9 +159,9 @@
'batch_no': cstr(p.batch_no).strip(),
'serial_no': cstr(p.serial_no).strip(),
'name': d.name
- })
+ }))
else:
- il.append({
+ il.append(webnotes._dict({
'warehouse': d.warehouse,
'reserved_warehouse': reserved_warehouse,
'item_code': d.item_code,
@@ -171,7 +171,7 @@
'batch_no': cstr(d.batch_no).strip(),
'serial_no': cstr(d.serial_no).strip(),
'name': d.name
- })
+ }))
return il
def get_already_delivered_qty(self, dn, so, so_detail):
diff --git a/selling/doctype/sms_center/sms_center.py b/selling/doctype/sms_center/sms_center.py
index 8b404e6..e834042 100644
--- a/selling/doctype/sms_center/sms_center.py
+++ b/selling/doctype/sms_center/sms_center.py
@@ -55,7 +55,7 @@
for d in rec:
rec_list += d[0] + ' - ' + d[1] + '\n'
self.doc.receiver_list = rec_list
- webnotes.errprint(rec_list)
+
def get_receiver_nos(self):
receiver_nos = []
for d in self.doc.receiver_list.split('\n'):
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index 25a70b6..d186da8 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -213,7 +213,7 @@
self.update_prevdoc_status()
# create stock ledger entry
- self.update_stock_ledger(update_stock = 1)
+ self.update_stock_ledger()
self.credit_limit()
@@ -258,7 +258,7 @@
self.update_prevdoc_status()
- self.update_stock_ledger(update_stock = -1)
+ self.update_stock_ledger()
webnotes.conn.set(self.doc, 'status', 'Cancelled')
self.cancel_packing_slips()
@@ -292,57 +292,32 @@
webnotes.msgprint(_("Packing Slip(s) Cancelled"))
- def update_stock_ledger(self, update_stock):
- self.values = []
+ def update_stock_ledger(self):
+ sl_entries = []
for d in self.get_item_list():
- if webnotes.conn.get_value("Item", d['item_code'], "is_stock_item") == "Yes":
- # this happens when item is changed from non-stock to stock item
- if not d["warehouse"]:
- continue
-
+ if d.item_code in self.stock_items and d.warehouse:
if d['reserved_qty'] < 0 :
# Reduce reserved qty from reserved warehouse mentioned in so
args = {
"item_code": d['item_code'],
"voucher_type": self.doc.doctype,
"voucher_no": self.doc.name,
- "reserved_qty": flt(update_stock) * flt(d['reserved_qty']),
+ "reserved_qty": (self.doc.docstatus==1 and 1 or -1)*flt(d['reserved_qty']),
"posting_date": self.doc.posting_date,
"is_amended": self.doc.amended_from and 'Yes' or 'No'
}
get_obj("Warehouse", d["reserved_warehouse"]).update_bin(args)
# Reduce actual qty from warehouse
- self.make_sl_entry(d, d['warehouse'], - flt(d['qty']) , 0, update_stock)
+ sl_entries.append(self.get_sl_entries(d, {
+ "actual_qty": -1*flt(d['qty']),
+ }))
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
-
+ self.make_sl_entries(sl_entries)
def get_item_list(self):
return get_obj('Sales Common').get_item_list(self)
-
- def make_sl_entry(self, d, wh, qty, in_value, update_stock):
- self.values.append({
- 'item_code' : d['item_code'],
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Delivery Note',
- 'voucher_no' : self.doc.name,
- 'voucher_detail_no' : d['name'],
- 'actual_qty' : qty,
- 'stock_uom' : d['uom'],
- 'incoming_rate' : in_value,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : (update_stock==1) and 'No' or 'Yes',
- 'batch_no' : d['batch_no'],
- 'serial_no' : d['serial_no'],
- "project" : self.doc.project_name
- })
-
-
def credit_limit(self):
"""check credit limit of items in DN Detail which are not fetched from sales order"""
amount, total = 0, 0
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py
index 703929c..75122db 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -170,14 +170,11 @@
d.rejected_serial_no = cstr(d.rejected_serial_no).strip().replace(',', '\n')
d.save()
- def update_stock(self, is_submit):
+ def update_stock(self):
pc_obj = get_obj('Purchase Common')
- self.values = []
+ sl_entries = []
for d in getlist(self.doclist, 'purchase_receipt_details'):
- if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes":
- if not d.warehouse:
- continue
-
+ if d.item_code in self.stock_items and d.warehouse:
ord_qty = 0
pr_qty = flt(d.qty) * flt(d.conversion_factor)
@@ -200,51 +197,27 @@
args = {
"item_code": d.item_code,
"posting_date": self.doc.posting_date,
- "ordered_qty": (is_submit and 1 or -1) * flt(ord_qty)
+ "ordered_qty": (self.doc.docstatus==1 and 1 or -1) * flt(ord_qty)
}
get_obj("Warehouse", d.warehouse).update_bin(args)
- # UPDATE actual qty to warehouse by pr_qty
if pr_qty:
- self.make_sl_entry(d, d.warehouse, flt(pr_qty), d.valuation_rate, is_submit)
-
- # UPDATE actual to rejected warehouse by rejected qty
+ sl_entries.append(self.get_sl_entries(d, {
+ "actual_qty": flt(pr_qty),
+ "serial_no": cstr(d.serial_no).strip()
+ }))
+
if flt(d.rejected_qty) > 0:
- self.make_sl_entry(d, self.doc.rejected_warehouse, flt(d.rejected_qty) * flt(d.conversion_factor), d.valuation_rate, is_submit, rejected = 1)
+ sl_entries.append(self.get_sl_entries(d, {
+ "warehouse": self.doc.rejected_warehouse,
+ "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
+ "serial_no": cstr(d.rejected_serial_no).strip()
+ }))
- self.bk_flush_supp_wh(is_submit)
-
- if self.values:
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
-
-
- # make Stock Entry
- def make_sl_entry(self, d, wh, qty, in_value, is_submit, rejected = 0):
- if rejected:
- serial_no = cstr(d.rejected_serial_no).strip()
- else:
- serial_no = cstr(d.serial_no).strip()
-
- self.values.append({
- 'item_code' : d.fields.has_key('item_code') and d.item_code or d.rm_item_code,
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Purchase Receipt',
- 'voucher_no' : self.doc.name,
- 'voucher_detail_no' : d.name,
- 'actual_qty' : qty,
- 'stock_uom' : d.stock_uom,
- 'incoming_rate' : in_value,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : (is_submit==1) and 'No' or 'Yes',
- 'batch_no' : cstr(d.batch_no).strip(),
- 'serial_no' : serial_no,
- "project" : d.project_name
- })
-
-
+ self.bk_flush_supp_wh(sl_entries)
+
+ self.make_sl_entries(sl_entries)
+
def validate_inspection(self):
for d in getlist(self.doclist, 'purchase_receipt_details'): #Enter inspection date for all items that require inspection
ins_reqd = sql("select inspection_required from `tabItem` where name = %s",
@@ -278,7 +251,7 @@
get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 1, is_incoming = 1)
# Update Stock
- self.update_stock(is_submit = 1)
+ self.update_stock()
# Update last purchase rate
purchase_controller.update_last_purchase_rate(self, 1)
@@ -310,23 +283,23 @@
# 3. Cancel Serial No
get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 0, is_incoming = 1)
- # 4.Update Bin
- self.update_stock(is_submit = 0)
-
+ self.update_stock()
self.update_prevdoc_status()
-
- # 6. Update last purchase rate
pc_obj.update_last_purchase_rate(self, 0)
self.make_cancel_gl_entries()
- def bk_flush_supp_wh(self, is_submit):
+ def bk_flush_supp_wh(self, sl_entries):
for d in getlist(self.doclist, 'pr_raw_material_details'):
# negative quantity is passed as raw material qty has to be decreased
# when PR is submitted and it has to be increased when PR is cancelled
- consumed_qty = - flt(d.consumed_qty)
- self.make_sl_entry(d, self.doc.supplier_warehouse, flt(consumed_qty), 0, is_submit)
-
+ sl_entries.append(self.get_sl_entries(d, {
+ "item_code": d.rm_item_code,
+ "warehouse": self.doc.supplier_warehouse,
+ "actual_qty": -1*flt(consumed_qty),
+ "incoming_rate": 0
+ }))
+
def get_current_stock(self):
for d in getlist(self.doclist, 'pr_raw_material_details'):
if self.doc.supplier_warehouse:
diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py
index 09181db..697a555 100644
--- a/stock/doctype/serial_no/serial_no.py
+++ b/stock/doctype/serial_no/serial_no.py
@@ -75,8 +75,7 @@
self.make_gl_entries()
def make_stock_ledger_entry(self, qty):
- from webnotes.model.code import get_obj
- values = [{
+ sl_entries = [{
'item_code' : self.doc.item_code,
'warehouse' : self.doc.warehouse,
'posting_date' : self.doc.purchase_date or (self.doc.creation and self.doc.creation.split(' ')[0]) or nowdate(),
@@ -93,8 +92,8 @@
'batch_no' : '',
'serial_no' : self.doc.name
}]
- get_obj('Stock Ledger').update_stock(values)
-
+
+ self.make_sl_entries(sl_entries)
def on_trash(self):
if self.doc.status == 'Delivered':
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index afa3eb4..9065f41 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -65,13 +65,13 @@
def on_submit(self):
self.update_serial_no(1)
- self.update_stock_ledger(0)
+ self.update_stock_ledger()
self.update_production_order(1)
self.make_gl_entries()
def on_cancel(self):
self.update_serial_no(0)
- self.update_stock_ledger(1)
+ self.update_stock_ledger()
self.update_production_order(0)
self.make_cancel_gl_entries()
@@ -351,16 +351,24 @@
serial_doc.docstatus = is_submit and 2 or 0
serial_doc.save()
- def update_stock_ledger(self, is_cancelled=0):
- self.values = []
+ def update_stock_ledger(self):
+ sl_entries = []
for d in getlist(self.doclist, 'mtn_details'):
if cstr(d.s_warehouse):
- self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
+ sl_entries.append(self.get_sl_entries(d, {
+ "warehouse": cstr(d.s_warehouse),
+ "actual_qty": -flt(d.transfer_qty),
+ "incoming_rate": flt(d.incoming_rate)
+ }))
+
if cstr(d.t_warehouse):
- self.add_to_values(d, cstr(d.t_warehouse), flt(d.transfer_qty), is_cancelled)
-
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values,
- self.doc.amended_from and 'Yes' or 'No')
+ sl_entries.append(self.get_sl_entries(d, {
+ "warehouse": cstr(d.t_warehouse),
+ "actual_qty": flt(d.transfer_qty),
+ "incoming_rate": flt(d.incoming_rate)
+ }))
+
+ self.make_sl_entries(sl_entries, self.doc.amended_from and 'Yes' or 'No')
def update_production_order(self, is_submit):
if self.doc.production_order:
@@ -632,26 +640,6 @@
# to be assigned for finished item
se_child.bom_no = bom_no
- def add_to_values(self, d, wh, qty, is_cancelled):
- self.values.append({
- 'item_code': d.item_code,
- 'warehouse': wh,
- 'posting_date': self.doc.posting_date,
- 'posting_time': self.doc.posting_time,
- 'voucher_type': 'Stock Entry',
- 'voucher_no': self.doc.name,
- 'voucher_detail_no': d.name,
- 'actual_qty': qty,
- 'incoming_rate': flt(d.incoming_rate, 2) or 0,
- 'stock_uom': d.stock_uom,
- 'company': self.doc.company,
- 'is_cancelled': (is_cancelled ==1) and 'Yes' or 'No',
- 'batch_no': cstr(d.batch_no).strip(),
- 'serial_no': cstr(d.serial_no).strip(),
- "project": self.doc.project_name,
- "fiscal_year": self.doc.fiscal_year,
- })
-
def get_cust_values(self):
"""fetches customer details"""
if self.doc.delivery_note_no:
diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py
index 5b3d660..ea38364 100644
--- a/stock/doctype/stock_ledger/stock_ledger.py
+++ b/stock/doctype/stock_ledger/stock_ledger.py
@@ -190,7 +190,7 @@
self.update_serial_purchase_details(obj, d, a, is_submit, rejected=True)
- def update_stock(self, values, is_amended = 'No'):
+ def update_stock(self, values, is_amended='No'):
for v in values:
sle_id, valid_serial_nos = '', ''
# get serial nos
diff --git a/stock/doctype/warehouse/warehouse.txt b/stock/doctype/warehouse/warehouse.txt
index 631b968..1b1fc33 100644
--- a/stock/doctype/warehouse/warehouse.txt
+++ b/stock/doctype/warehouse/warehouse.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-03-07 18:50:32",
"docstatus": 0,
- "modified": "2013-07-23 12:01:16",
+ "modified": "2013-08-01 15:27:49",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -20,8 +20,7 @@
"name": "__common__",
"parent": "Warehouse",
"parentfield": "fields",
- "parenttype": "DocType",
- "read_only": 0
+ "parenttype": "DocType"
},
{
"doctype": "DocPerm",
@@ -29,9 +28,9 @@
"parent": "Warehouse",
"parentfield": "permissions",
"parenttype": "DocType",
- "permlevel": 0,
"read": 1,
- "report": 1
+ "report": 1,
+ "submit": 0
},
{
"doctype": "DocType",
@@ -43,7 +42,8 @@
"fieldtype": "Section Break",
"label": "Warehouse Detail",
"oldfieldtype": "Section Break",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -53,6 +53,7 @@
"oldfieldname": "warehouse_name",
"oldfieldtype": "Data",
"permlevel": 0,
+ "read_only": 0,
"reqd": 1
},
{
@@ -65,14 +66,25 @@
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
+ "read_only": 0,
"reqd": 1,
"search_index": 1
},
{
+ "description": "This account will be used for perpetual accounting for inventory e.g. Stock-in-Hand, Fixed Asset Account etc",
+ "doctype": "DocField",
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "label": "Account",
+ "options": "Account",
+ "permlevel": 0
+ },
+ {
"doctype": "DocField",
"fieldname": "column_break_4",
"fieldtype": "Section Break",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"description": "If set, data entry is only allowed for specified users. Else, entry is allowed for all users with requisite permissions.",
@@ -81,7 +93,8 @@
"fieldtype": "Table",
"label": "Warehouse Users",
"options": "Warehouse User",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"description": "For Reference Only.",
@@ -89,7 +102,8 @@
"fieldname": "warehouse_contact_info",
"fieldtype": "Section Break",
"label": "Warehouse Contact Info",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -100,7 +114,8 @@
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
- "print_hide": 0
+ "print_hide": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -110,7 +125,8 @@
"oldfieldname": "phone_no",
"oldfieldtype": "Int",
"options": "Phone",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -120,14 +136,16 @@
"oldfieldname": "mobile_no",
"oldfieldtype": "Int",
"options": "Phone",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -136,7 +154,8 @@
"label": "Address Line 1",
"oldfieldname": "address_line_1",
"oldfieldtype": "Data",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -145,7 +164,8 @@
"label": "Address Line 2",
"oldfieldname": "address_line_2",
"oldfieldtype": "Data",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -156,6 +176,7 @@
"oldfieldname": "city",
"oldfieldtype": "Data",
"permlevel": 0,
+ "read_only": 0,
"reqd": 0
},
{
@@ -166,7 +187,8 @@
"oldfieldname": "state",
"oldfieldtype": "Select",
"options": "Suggest",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -175,7 +197,8 @@
"label": "PIN",
"oldfieldname": "pin",
"oldfieldtype": "Int",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"description": "This feature is for merging duplicate warehouses. It will replace all the links of this warehouse by \"Merge Into\" warehouse. After merging you can delete this warehouse, as stock level for this warehouse will be zero.",
@@ -183,7 +206,8 @@
"fieldname": "merge_warehouses_section",
"fieldtype": "Section Break",
"label": "Merge Warehouses",
- "permlevel": 2
+ "permlevel": 2,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -191,22 +215,32 @@
"fieldtype": "Link",
"label": "Merge Into",
"options": "Warehouse",
- "permlevel": 2
+ "permlevel": 2,
+ "read_only": 0
},
{
"doctype": "DocField",
"fieldname": "merge",
"fieldtype": "Button",
"label": "Merge",
- "permlevel": 2
+ "permlevel": 2,
+ "read_only": 0
},
{
"amend": 0,
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
+ "permlevel": 0,
"role": "Material Master Manager",
- "submit": 0,
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "doctype": "DocPerm",
+ "permlevel": 0,
+ "role": "System Manager",
"write": 1
},
{
@@ -214,20 +248,26 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "role": "Material User",
- "submit": 0,
+ "permlevel": 0,
+ "role": "Material Manager",
"write": 0
},
{
+ "amend": 0,
+ "cancel": 0,
+ "create": 0,
"doctype": "DocPerm",
- "role": "Sales User"
+ "permlevel": 0,
+ "role": "Material User",
+ "write": 0
},
{
+ "amend": 0,
+ "cancel": 0,
+ "create": 0,
"doctype": "DocPerm",
- "role": "Purchase User"
- },
- {
- "doctype": "DocPerm",
- "role": "Accounts User"
+ "permlevel": 2,
+ "role": "System Manager",
+ "write": 1
}
]
\ No newline at end of file
diff --git a/stock/utils.py b/stock/utils.py
index da4752e..0b42e0f 100644
--- a/stock/utils.py
+++ b/stock/utils.py
@@ -21,6 +21,35 @@
from webnotes.defaults import get_global_default
from webnotes.utils.email_lib import sendmail
+
+def get_stock_balance_on(warehouse_list, posting_date=None):
+ if not posting_date: posting_date = nowdate()
+
+ stock_ledger_entries = webnotes.conn.sql("""
+ SELECT
+ item_code, warehouse, stock_value
+ FROM
+ `tabStock Ledger Entry`
+ WHERE
+ warehouse in (%s)
+ AND posting_date <= %s
+ ORDER BY timestamp(posting_date, posting_time) DESC, name DESC
+ """ % (', '.join(['%s']*len(warehouse_list)), '%s'),
+ tuple(warehouse_list + [posting_date]), as_dict=1)
+
+ sle_map = {}
+ for sle in stock_ledger_entries:
+ sle_map.setdefault(sle.warehouse, {}).setdefault(sle.item_code, flt(sle.stock_value))
+
+ return sum([sum(item_dict.values()) for item_dict in sle_map.values()])
+
+def get_latest_stock_balance(warehouse_list):
+ return webnotes.conn.sql("""
+ SELECT sum(stock_value)
+ FROM tabBin
+ where warehouse in (%s)
+ """ % ', '.join(['%s']*len(warehouse_list)), warehouse_list)[0][0]
+
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
if not end_of_life:
end_of_life = webnotes.conn.get_value("Item", item_code, "end_of_life")