commit
cfafe93391
@ -14,6 +14,9 @@ class AccountsSettings(Document):
|
|||||||
frappe.db.set_default("auto_accounting_for_stock", self.auto_accounting_for_stock)
|
frappe.db.set_default("auto_accounting_for_stock", self.auto_accounting_for_stock)
|
||||||
|
|
||||||
if cint(self.auto_accounting_for_stock):
|
if cint(self.auto_accounting_for_stock):
|
||||||
|
if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")):
|
||||||
|
frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory, please disable it from Stock Settings"))
|
||||||
|
|
||||||
# set default perpetual account in company
|
# set default perpetual account in company
|
||||||
for company in frappe.db.sql("select name from tabCompany"):
|
for company in frappe.db.sql("select name from tabCompany"):
|
||||||
frappe.get_doc("Company", company[0]).save()
|
frappe.get_doc("Company", company[0]).save()
|
||||||
|
@ -7,7 +7,7 @@ import frappe.defaults
|
|||||||
from frappe.utils import cint, cstr, flt
|
from frappe.utils import cint, cstr, flt
|
||||||
from frappe import _, msgprint, throw
|
from frappe import _, msgprint, throw
|
||||||
from erpnext.accounts.party import get_party_account, get_due_date
|
from erpnext.accounts.party import get_party_account, get_due_date
|
||||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
from erpnext.controllers.stock_controller import update_gl_entries_after, block_negative_stock
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
from erpnext.controllers.selling_controller import SellingController
|
from erpnext.controllers.selling_controller import SellingController
|
||||||
@ -456,8 +456,8 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
self.make_sl_entries(sl_entries)
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
def make_gl_entries(self, repost_future_gle=True):
|
def make_gl_entries(self, repost_future_gle=True, allow_negative_stock=False):
|
||||||
gl_entries = self.get_gl_entries()
|
gl_entries = self.get_gl_entries(allow_negative_stock=allow_negative_stock)
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
@ -476,7 +476,7 @@ class SalesInvoice(SellingController):
|
|||||||
items, warehouses = self.get_items_and_warehouses()
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
|
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None):
|
def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False):
|
||||||
from erpnext.accounts.general_ledger import merge_similar_entries
|
from erpnext.accounts.general_ledger import merge_similar_entries
|
||||||
|
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
@ -485,7 +485,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
|
|
||||||
self.make_item_gl_entries(gl_entries)
|
self.make_item_gl_entries(gl_entries, allow_negative_stock)
|
||||||
|
|
||||||
# merge gl entries before adding pos entries
|
# merge gl entries before adding pos entries
|
||||||
gl_entries = merge_similar_entries(gl_entries)
|
gl_entries = merge_similar_entries(gl_entries)
|
||||||
@ -520,7 +520,7 @@ class SalesInvoice(SellingController):
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries, allow_negative_stock=False):
|
||||||
# income account gl entries
|
# income account gl entries
|
||||||
for item in self.get("entries"):
|
for item in self.get("entries"):
|
||||||
if flt(item.base_amount):
|
if flt(item.base_amount):
|
||||||
@ -537,7 +537,7 @@ class SalesInvoice(SellingController):
|
|||||||
# expense account gl entries
|
# expense account gl entries
|
||||||
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
|
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
|
||||||
and cint(self.update_stock):
|
and cint(self.update_stock):
|
||||||
gl_entries += super(SalesInvoice, self).get_gl_entries()
|
gl_entries += super(SalesInvoice, self).get_gl_entries(allow_negative_stock=allow_negative_stock)
|
||||||
|
|
||||||
def make_pos_gl_entries(self, gl_entries):
|
def make_pos_gl_entries(self, gl_entries):
|
||||||
if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
|
if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
|
||||||
|
@ -97,8 +97,7 @@ def validate_account_for_auto_accounting_for_stock(gl_map):
|
|||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
if entry.account in aii_accounts:
|
if entry.account in aii_accounts:
|
||||||
frappe.throw(_("Account: {0} can only be updated via \
|
frappe.throw(_("Account: {0} can only be updated via Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||||
Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||||
|
@ -8,10 +8,10 @@ from frappe import msgprint, _
|
|||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||||
|
|
||||||
class StockController(AccountsController):
|
class StockController(AccountsController):
|
||||||
def make_gl_entries(self, repost_future_gle=True):
|
def make_gl_entries(self, repost_future_gle=True, allow_negative_stock=False):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||||
|
|
||||||
@ -19,16 +19,19 @@ class StockController(AccountsController):
|
|||||||
warehouse_account = get_warehouse_account()
|
warehouse_account = get_warehouse_account()
|
||||||
|
|
||||||
if self.docstatus==1:
|
if self.docstatus==1:
|
||||||
gl_entries = self.get_gl_entries(warehouse_account)
|
gl_entries = self.get_gl_entries(warehouse_account, allow_negative_stock=allow_negative_stock)
|
||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
|
|
||||||
if repost_future_gle:
|
if repost_future_gle:
|
||||||
items, warehouses = self.get_items_and_warehouses()
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account)
|
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
|
||||||
|
warehouse_account, allow_negative_stock)
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
||||||
default_cost_center=None):
|
default_cost_center=None, allow_negative_stock=False):
|
||||||
from erpnext.accounts.general_ledger import process_gl_map
|
|
||||||
|
# block_negative_stock(allow_negative_stock)
|
||||||
|
|
||||||
if not warehouse_account:
|
if not warehouse_account:
|
||||||
warehouse_account = get_warehouse_account()
|
warehouse_account = get_warehouse_account()
|
||||||
|
|
||||||
@ -46,12 +49,17 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
self.check_expense_account(detail)
|
self.check_expense_account(detail)
|
||||||
|
|
||||||
|
stock_value_difference = flt(sle.stock_value_difference, 2)
|
||||||
|
if not stock_value_difference:
|
||||||
|
valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, sle.posting_date)
|
||||||
|
stock_value_difference = flt(sle.actual_qty)*flt(valuation_rate)
|
||||||
|
|
||||||
gl_list.append(self.get_gl_dict({
|
gl_list.append(self.get_gl_dict({
|
||||||
"account": warehouse_account[sle.warehouse],
|
"account": warehouse_account[sle.warehouse],
|
||||||
"against": detail.expense_account,
|
"against": detail.expense_account,
|
||||||
"cost_center": detail.cost_center,
|
"cost_center": detail.cost_center,
|
||||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
"debit": flt(sle.stock_value_difference, 2)
|
"debit": stock_value_difference
|
||||||
}))
|
}))
|
||||||
|
|
||||||
# to target warehouse / expense account
|
# to target warehouse / expense account
|
||||||
@ -60,7 +68,7 @@ class StockController(AccountsController):
|
|||||||
"against": warehouse_account[sle.warehouse],
|
"against": warehouse_account[sle.warehouse],
|
||||||
"cost_center": detail.cost_center,
|
"cost_center": detail.cost_center,
|
||||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
"credit": flt(sle.stock_value_difference, 2)
|
"credit": stock_value_difference
|
||||||
}))
|
}))
|
||||||
elif sle.warehouse not in warehouse_with_no_account:
|
elif sle.warehouse not in warehouse_with_no_account:
|
||||||
warehouse_with_no_account.append(sle.warehouse)
|
warehouse_with_no_account.append(sle.warehouse)
|
||||||
@ -118,7 +126,8 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
def get_stock_ledger_details(self):
|
def get_stock_ledger_details(self):
|
||||||
stock_ledger = {}
|
stock_ledger = {}
|
||||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference, voucher_detail_no
|
for sle in frappe.db.sql("""select warehouse, stock_value_difference,
|
||||||
|
voucher_detail_no, item_code, posting_date, actual_qty
|
||||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||||
(self.doctype, self.name), as_dict=True):
|
(self.doctype, self.name), as_dict=True):
|
||||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||||
@ -214,7 +223,8 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
return serialized_items
|
return serialized_items
|
||||||
|
|
||||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None):
|
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||||
|
warehouse_account=None, allow_negative_stock=False):
|
||||||
def _delete_gl_entries(voucher_type, voucher_no):
|
def _delete_gl_entries(voucher_type, voucher_no):
|
||||||
frappe.db.sql("""delete from `tabGL Entry`
|
frappe.db.sql("""delete from `tabGL Entry`
|
||||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||||
@ -228,12 +238,12 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
|
|||||||
for voucher_type, voucher_no in future_stock_vouchers:
|
for voucher_type, voucher_no in future_stock_vouchers:
|
||||||
existing_gle = gle.get((voucher_type, voucher_no), [])
|
existing_gle = gle.get((voucher_type, voucher_no), [])
|
||||||
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
|
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
|
||||||
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
|
expected_gle = voucher_obj.get_gl_entries(warehouse_account, allow_negative_stock=allow_negative_stock)
|
||||||
if expected_gle:
|
if expected_gle:
|
||||||
if not existing_gle or not compare_existing_and_expected_gle(existing_gle,
|
if not existing_gle or not compare_existing_and_expected_gle(existing_gle,
|
||||||
expected_gle):
|
expected_gle):
|
||||||
_delete_gl_entries(voucher_type, voucher_no)
|
_delete_gl_entries(voucher_type, voucher_no)
|
||||||
voucher_obj.make_gl_entries(repost_future_gle=False)
|
voucher_obj.make_gl_entries(repost_future_gle=False, allow_negative_stock=allow_negative_stock)
|
||||||
else:
|
else:
|
||||||
_delete_gl_entries(voucher_type, voucher_no)
|
_delete_gl_entries(voucher_type, voucher_no)
|
||||||
|
|
||||||
@ -285,3 +295,22 @@ def get_warehouse_account():
|
|||||||
warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount
|
warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount
|
||||||
where account_type = 'Warehouse' and ifnull(master_name, '') != ''"""))
|
where account_type = 'Warehouse' and ifnull(master_name, '') != ''"""))
|
||||||
return warehouse_account
|
return warehouse_account
|
||||||
|
|
||||||
|
def block_negative_stock(allow_negative_stock=False):
|
||||||
|
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) and not allow_negative_stock:
|
||||||
|
if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")):
|
||||||
|
frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory, please disable it from Stock Settings"))
|
||||||
|
|
||||||
|
def get_valuation_rate(item_code, warehouse, posting_date):
|
||||||
|
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||||
|
from `tabStock Ledger Entry`
|
||||||
|
where item_code = %s and warehouse = %s
|
||||||
|
and ifnull(qty_after_transaction, 0) > 0 and posting_date < %s
|
||||||
|
order by posting_date desc limit 1""", (item_code, warehouse, posting_date))
|
||||||
|
|
||||||
|
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
|
||||||
|
|
||||||
|
if not valuation_rate:
|
||||||
|
valuation_rate = frappe.db.get_value("Item Price", {"item_code": item_code, "buying": 1}, "price_list_rate")
|
||||||
|
|
||||||
|
return valuation_rate
|
||||||
|
@ -138,10 +138,18 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
|||||||
item.valuation_method : sys_defaults.valuation_method;
|
item.valuation_method : sys_defaults.valuation_method;
|
||||||
var is_fifo = valuation_method == "FIFO";
|
var is_fifo = valuation_method == "FIFO";
|
||||||
|
|
||||||
|
if(sl.voucher_type=="Stock Reconciliation") {
|
||||||
|
var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
|
||||||
|
} else {
|
||||||
var diff = me.get_value_diff(wh, sl, is_fifo);
|
var diff = me.get_value_diff(wh, sl, is_fifo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(sl.voucher_type=="Stock Reconciliation") {
|
||||||
|
var diff = sl.qty_after_transaction - item.closing_qty_value;
|
||||||
} else {
|
} else {
|
||||||
var diff = sl.qty;
|
var diff = sl.qty;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(posting_datetime < from_date) {
|
if(posting_datetime < from_date) {
|
||||||
item.opening += diff;
|
item.opening += diff;
|
||||||
@ -150,6 +158,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
|||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.closing_qty_value += diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -78,7 +78,8 @@ data_map = {
|
|||||||
"Stock Ledger Entry": {
|
"Stock Ledger Entry": {
|
||||||
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
|
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
|
||||||
"actual_qty as qty", "voucher_type", "voucher_no", "project",
|
"actual_qty as qty", "voucher_type", "voucher_no", "project",
|
||||||
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no"],
|
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no",
|
||||||
|
"qty_after_transaction", "valuation_rate"],
|
||||||
"order_by": "posting_date, posting_time, name",
|
"order_by": "posting_date, posting_time, name",
|
||||||
"links": {
|
"links": {
|
||||||
"item_code": ["Item", "name"],
|
"item_code": ["Item", "name"],
|
||||||
|
@ -26,7 +26,7 @@ class Bin(Document):
|
|||||||
def update_stock(self, args):
|
def update_stock(self, args):
|
||||||
self.update_qty(args)
|
self.update_qty(args)
|
||||||
|
|
||||||
if args.get("actual_qty"):
|
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
||||||
from erpnext.stock.stock_ledger import update_entries_after
|
from erpnext.stock.stock_ledger import update_entries_after
|
||||||
|
|
||||||
if not args.get("posting_date"):
|
if not args.get("posting_date"):
|
||||||
@ -42,8 +42,21 @@ class Bin(Document):
|
|||||||
|
|
||||||
def update_qty(self, args):
|
def update_qty(self, args):
|
||||||
# update the stock values (for current quantities)
|
# update the stock values (for current quantities)
|
||||||
|
if args.get("voucher_type")=="Stock Reconciliation":
|
||||||
|
if args.get('is_cancelled') == 'No':
|
||||||
|
self.actual_qty = args.get("qty_after_transaction")
|
||||||
|
else:
|
||||||
|
qty_after_transaction = frappe.db.get_value("""select qty_after_transaction
|
||||||
|
from `tabStock Ledger Entry`
|
||||||
|
where item_code=%s and warehouse=%s
|
||||||
|
and not (voucher_type='Stock Reconciliation' and voucher_no=%s)
|
||||||
|
order by posting_date desc limit 1""",
|
||||||
|
(self.item_code, self.warehouse, args.get('voucher_no')))
|
||||||
|
|
||||||
|
self.actual_qty = flt(qty_after_transaction[0][0]) if qty_after_transaction else 0.0
|
||||||
|
else:
|
||||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
||||||
|
|
||||||
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
||||||
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
||||||
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
||||||
|
@ -97,10 +97,10 @@ class LandedCostVoucher(Document):
|
|||||||
|
|
||||||
# update stock & gl entries for cancelled state of PR
|
# update stock & gl entries for cancelled state of PR
|
||||||
pr.docstatus = 2
|
pr.docstatus = 2
|
||||||
pr.update_stock()
|
pr.update_stock_ledger()
|
||||||
pr.make_gl_entries_on_cancel()
|
pr.make_gl_entries_on_cancel()
|
||||||
|
|
||||||
# update stock & gl entries for submit state of PR
|
# update stock & gl entries for submit state of PR
|
||||||
pr.docstatus = 1
|
pr.docstatus = 1
|
||||||
pr.update_stock()
|
pr.update_stock_ledger()
|
||||||
pr.make_gl_entries()
|
pr.make_gl_entries()
|
||||||
|
@ -130,7 +130,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if not d.prevdoc_docname:
|
if not d.prevdoc_docname:
|
||||||
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
def update_stock(self):
|
def update_stock_ledger(self):
|
||||||
sl_entries = []
|
sl_entries = []
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
|
|
||||||
self.update_stock()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||||
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
||||||
@ -267,7 +267,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
|
|
||||||
self.update_stock()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
pc_obj.update_last_purchase_rate(self, 0)
|
pc_obj.update_last_purchase_rate(self, 0)
|
||||||
@ -283,8 +283,11 @@ class PurchaseReceipt(BuyingController):
|
|||||||
def get_rate(self,arg):
|
def get_rate(self,arg):
|
||||||
return frappe.get_doc('Purchase Common').get_rate(arg,self)
|
return frappe.get_doc('Purchase Common').get_rate(arg,self)
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None):
|
def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False):
|
||||||
from erpnext.accounts.general_ledger import process_gl_map
|
from erpnext.accounts.general_ledger import process_gl_map
|
||||||
|
from erpnext.controllers.stock_controller import block_negative_stock
|
||||||
|
|
||||||
|
block_negative_stock(allow_negative_stock)
|
||||||
|
|
||||||
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
|
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||||
|
@ -527,7 +527,7 @@ class StockEntry(StockController):
|
|||||||
}
|
}
|
||||||
}, bom_no=self.bom_no)
|
}, bom_no=self.bom_no)
|
||||||
|
|
||||||
self.get_stock_and_rate()
|
self.e()
|
||||||
|
|
||||||
def get_bom_raw_materials(self, qty):
|
def get_bom_raw_materials(self, qty):
|
||||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
||||||
|
@ -44,11 +44,14 @@ class StockLedgerEntry(Document):
|
|||||||
formatdate(self.posting_date), self.posting_time))
|
formatdate(self.posting_date), self.posting_time))
|
||||||
|
|
||||||
def validate_mandatory(self):
|
def validate_mandatory(self):
|
||||||
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
|
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','company']
|
||||||
for k in mandatory:
|
for k in mandatory:
|
||||||
if not self.get(k):
|
if not self.get(k):
|
||||||
frappe.throw(_("{0} is required").format(self.meta.get_label(k)))
|
frappe.throw(_("{0} is required").format(self.meta.get_label(k)))
|
||||||
|
|
||||||
|
if self.voucher_type != "Stock Reconciliation" and not self.actual_qty:
|
||||||
|
frappe.throw(_("Actual Qty is mandatory"))
|
||||||
|
|
||||||
def validate_item(self):
|
def validate_item(self):
|
||||||
item_det = frappe.db.sql("""select name, has_batch_no, docstatus, is_stock_item
|
item_det = frappe.db.sql("""select name, has_batch_no, docstatus, is_stock_item
|
||||||
from tabItem where name=%s""", self.item_code, as_dict=True)[0]
|
from tabItem where name=%s""", self.item_code, as_dict=True)[0]
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"default": "Today",
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
@ -118,7 +119,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2014-05-26 03:05:54.024413",
|
"modified": "2014-10-07 12:43:52.825575",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Reconciliation",
|
"name": "Stock Reconciliation",
|
||||||
|
@ -22,7 +22,7 @@ class StockReconciliation(StockController):
|
|||||||
self.validate_expense_account()
|
self.validate_expense_account()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.insert_stock_ledger_entries()
|
self.update_stock_ledger()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
@ -126,10 +126,9 @@ class StockReconciliation(StockController):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e))
|
self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e))
|
||||||
|
|
||||||
def insert_stock_ledger_entries(self):
|
def update_stock_ledger(self):
|
||||||
""" find difference between current and expected entries
|
""" find difference between current and expected entries
|
||||||
and create stock ledger entries based on the difference"""
|
and create stock ledger entries based on the difference"""
|
||||||
from erpnext.stock.utils import get_valuation_method
|
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle
|
from erpnext.stock.stock_ledger import get_previous_sle
|
||||||
|
|
||||||
row_template = ["item_code", "warehouse", "qty", "valuation_rate"]
|
row_template = ["item_code", "warehouse", "qty", "valuation_rate"]
|
||||||
@ -141,6 +140,8 @@ class StockReconciliation(StockController):
|
|||||||
for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
|
for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
|
||||||
row = frappe._dict(zip(row_template, row))
|
row = frappe._dict(zip(row_template, row))
|
||||||
row["row_num"] = row_num
|
row["row_num"] = row_num
|
||||||
|
|
||||||
|
if row.qty in ("", None) or row.valuation_rate in ("", None):
|
||||||
previous_sle = get_previous_sle({
|
previous_sle = get_previous_sle({
|
||||||
"item_code": row.item_code,
|
"item_code": row.item_code,
|
||||||
"warehouse": row.warehouse,
|
"warehouse": row.warehouse,
|
||||||
@ -148,98 +149,18 @@ class StockReconciliation(StockController):
|
|||||||
"posting_time": self.posting_time
|
"posting_time": self.posting_time
|
||||||
})
|
})
|
||||||
|
|
||||||
# check valuation rate mandatory
|
if row.qty in ("", None):
|
||||||
if row.qty not in ["", None] and not row.valuation_rate and \
|
row.qty = previous_sle.get("qty_after_transaction")
|
||||||
flt(previous_sle.get("qty_after_transaction")) <= 0:
|
|
||||||
frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code))
|
|
||||||
|
|
||||||
change_in_qty = row.qty not in ["", None] and \
|
if row.valuation_rate in ("", None):
|
||||||
(flt(row.qty) - flt(previous_sle.get("qty_after_transaction")))
|
row.valuation_rate = previous_sle.get("valuation_rate")
|
||||||
|
|
||||||
change_in_rate = row.valuation_rate not in ["", None] and \
|
# if row.qty and not row.valuation_rate:
|
||||||
(flt(row.valuation_rate) - flt(previous_sle.get("valuation_rate")))
|
# frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code))
|
||||||
|
|
||||||
if get_valuation_method(row.item_code) == "Moving Average":
|
self.insert_entries(row)
|
||||||
self.sle_for_moving_avg(row, previous_sle, change_in_qty, change_in_rate)
|
|
||||||
|
|
||||||
else:
|
def insert_entries(self, row):
|
||||||
self.sle_for_fifo(row, previous_sle, change_in_qty, change_in_rate)
|
|
||||||
|
|
||||||
def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate):
|
|
||||||
"""Insert Stock Ledger Entries for Moving Average valuation"""
|
|
||||||
def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate):
|
|
||||||
if previous_valuation_rate == 0:
|
|
||||||
return flt(valuation_rate)
|
|
||||||
else:
|
|
||||||
if valuation_rate in ["", None]:
|
|
||||||
valuation_rate = previous_valuation_rate
|
|
||||||
return (qty * valuation_rate - previous_qty * previous_valuation_rate) \
|
|
||||||
/ flt(qty - previous_qty)
|
|
||||||
|
|
||||||
if change_in_qty:
|
|
||||||
# if change in qty, irrespective of change in rate
|
|
||||||
incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate),
|
|
||||||
flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("valuation_rate")))
|
|
||||||
|
|
||||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
|
|
||||||
self.insert_entries({"actual_qty": change_in_qty, "incoming_rate": incoming_rate}, row)
|
|
||||||
|
|
||||||
elif change_in_rate and flt(previous_sle.get("qty_after_transaction")) > 0:
|
|
||||||
# if no change in qty, but change in rate
|
|
||||||
# and positive actual stock before this reconciliation
|
|
||||||
incoming_rate = _get_incoming_rate(
|
|
||||||
flt(previous_sle.get("qty_after_transaction"))+1, flt(row.valuation_rate),
|
|
||||||
flt(previous_sle.get("qty_after_transaction")),
|
|
||||||
flt(previous_sle.get("valuation_rate")))
|
|
||||||
|
|
||||||
# +1 entry
|
|
||||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1"
|
|
||||||
self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row)
|
|
||||||
|
|
||||||
# -1 entry
|
|
||||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1"
|
|
||||||
self.insert_entries({"actual_qty": -1}, row)
|
|
||||||
|
|
||||||
def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate):
|
|
||||||
"""Insert Stock Ledger Entries for FIFO valuation"""
|
|
||||||
previous_stock_queue = json.loads(previous_sle.get("stock_queue") or "[]")
|
|
||||||
previous_stock_qty = sum((batch[0] for batch in previous_stock_queue))
|
|
||||||
previous_stock_value = sum((batch[0] * batch[1] for batch in \
|
|
||||||
previous_stock_queue))
|
|
||||||
|
|
||||||
def _insert_entries():
|
|
||||||
if previous_stock_queue != [[row.qty, row.valuation_rate]]:
|
|
||||||
# make entry as per attachment
|
|
||||||
if flt(row.qty):
|
|
||||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
|
|
||||||
self.insert_entries({"actual_qty": row.qty,
|
|
||||||
"incoming_rate": flt(row.valuation_rate)}, row)
|
|
||||||
|
|
||||||
# Make reverse entry
|
|
||||||
if previous_stock_qty:
|
|
||||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry"
|
|
||||||
self.insert_entries({"actual_qty": -1 * previous_stock_qty,
|
|
||||||
"incoming_rate": previous_stock_qty < 0 and
|
|
||||||
flt(row.valuation_rate) or 0}, row)
|
|
||||||
|
|
||||||
|
|
||||||
if change_in_qty:
|
|
||||||
if row.valuation_rate in ["", None]:
|
|
||||||
# dont want change in valuation
|
|
||||||
if previous_stock_qty > 0:
|
|
||||||
# set valuation_rate as previous valuation_rate
|
|
||||||
row.valuation_rate = previous_stock_value / flt(previous_stock_qty)
|
|
||||||
|
|
||||||
_insert_entries()
|
|
||||||
|
|
||||||
elif change_in_rate and previous_stock_qty > 0:
|
|
||||||
# if no change in qty, but change in rate
|
|
||||||
# and positive actual stock before this reconciliation
|
|
||||||
|
|
||||||
row.qty = previous_stock_qty
|
|
||||||
_insert_entries()
|
|
||||||
|
|
||||||
def insert_entries(self, opts, row):
|
|
||||||
"""Insert Stock Ledger Entries"""
|
"""Insert Stock Ledger Entries"""
|
||||||
args = frappe._dict({
|
args = frappe._dict({
|
||||||
"doctype": "Stock Ledger Entry",
|
"doctype": "Stock Ledger Entry",
|
||||||
@ -251,11 +172,11 @@ class StockReconciliation(StockController):
|
|||||||
"voucher_no": self.name,
|
"voucher_no": self.name,
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
|
"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
|
||||||
"voucher_detail_no": row.voucher_detail_no,
|
|
||||||
"fiscal_year": self.fiscal_year,
|
"fiscal_year": self.fiscal_year,
|
||||||
"is_cancelled": "No"
|
"is_cancelled": "No",
|
||||||
|
"qty_after_transaction": row.qty,
|
||||||
|
"valuation_rate": row.valuation_rate
|
||||||
})
|
})
|
||||||
args.update(opts)
|
|
||||||
self.make_sl_entries([args])
|
self.make_sl_entries([args])
|
||||||
|
|
||||||
# append to entries
|
# append to entries
|
||||||
@ -282,12 +203,12 @@ class StockReconciliation(StockController):
|
|||||||
"posting_time": self.posting_time
|
"posting_time": self.posting_time
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None):
|
def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False):
|
||||||
if not self.cost_center:
|
if not self.cost_center:
|
||||||
msgprint(_("Please enter Cost Center"), raise_exception=1)
|
msgprint(_("Please enter Cost Center"), raise_exception=1)
|
||||||
|
|
||||||
return super(StockReconciliation, self).get_gl_entries(warehouse_account,
|
return super(StockReconciliation, self).get_gl_entries(warehouse_account,
|
||||||
self.expense_account, self.cost_center)
|
self.expense_account, self.cost_center, allow_negative_stock=allow_negative_stock)
|
||||||
|
|
||||||
def validate_expense_account(self):
|
def validate_expense_account(self):
|
||||||
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||||
@ -295,7 +216,7 @@ class StockReconciliation(StockController):
|
|||||||
|
|
||||||
if not self.expense_account:
|
if not self.expense_account:
|
||||||
msgprint(_("Please enter Expense Account"), raise_exception=1)
|
msgprint(_("Please enter Expense Account"), raise_exception=1)
|
||||||
elif not frappe.db.sql("""select * from `tabStock Ledger Entry`"""):
|
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
|
||||||
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
|
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
|
||||||
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
|
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
|
||||||
|
|
||||||
|
@ -6,14 +6,16 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.utils import cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class StockSettings(Document):
|
class StockSettings(Document):
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
for key in ["item_naming_by", "item_group", "stock_uom",
|
if cint(self.allow_negative_stock) and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||||
"allow_negative_stock"]:
|
frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory"))
|
||||||
|
|
||||||
|
for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock"]:
|
||||||
frappe.db.set_default(key, self.get(key, ""))
|
frappe.db.set_default(key, self.get(key, ""))
|
||||||
|
|
||||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||||
@ -25,3 +27,5 @@ class StockSettings(Document):
|
|||||||
if submitted_stock_frozen > stock_frozen_limit:
|
if submitted_stock_frozen > stock_frozen_limit:
|
||||||
self.stock_frozen_upto_days = stock_frozen_limit
|
self.stock_frozen_upto_days = stock_frozen_limit
|
||||||
frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
|
frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,8 +104,15 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
|
|||||||
item.valuation_method : sys_defaults.valuation_method;
|
item.valuation_method : sys_defaults.valuation_method;
|
||||||
var is_fifo = valuation_method == "FIFO";
|
var is_fifo = valuation_method == "FIFO";
|
||||||
|
|
||||||
|
if(sl.voucher_type=="Stock Reconciliation") {
|
||||||
|
var qty_diff = sl.qty_after_transaction - (item.temp_closing_qty || 0.0);
|
||||||
|
var value_diff = (sl.valuation_rate * sl.qty_after_transaction) - (item.temp_closing_value || 0.0);
|
||||||
|
} else {
|
||||||
var qty_diff = sl.qty;
|
var qty_diff = sl.qty;
|
||||||
var value_diff = me.get_value_diff(wh, sl, is_fifo);
|
var value_diff = me.get_value_diff(wh, sl, is_fifo);
|
||||||
|
}
|
||||||
|
item.temp_closing_qty += qty_diff;
|
||||||
|
item.temp_closing_value += value_diff;
|
||||||
|
|
||||||
if(sl_posting_date < from_date) {
|
if(sl_posting_date < from_date) {
|
||||||
item.opening_qty += qty_diff;
|
item.opening_qty += qty_diff;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff
|
from frappe.utils import date_diff, flt
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
|
|
||||||
@ -42,9 +42,14 @@ def get_columns():
|
|||||||
|
|
||||||
def get_fifo_queue(filters):
|
def get_fifo_queue(filters):
|
||||||
item_details = {}
|
item_details = {}
|
||||||
|
prev_qty = 0.0
|
||||||
for d in get_stock_ledger_entries(filters):
|
for d in get_stock_ledger_entries(filters):
|
||||||
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
|
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
|
||||||
fifo_queue = item_details[d.name]["fifo_queue"]
|
fifo_queue = item_details[d.name]["fifo_queue"]
|
||||||
|
|
||||||
|
if d.voucher_type == "Stock Reconciliation":
|
||||||
|
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_qty)
|
||||||
|
|
||||||
if d.actual_qty > 0:
|
if d.actual_qty > 0:
|
||||||
fifo_queue.append([d.actual_qty, d.posting_date])
|
fifo_queue.append([d.actual_qty, d.posting_date])
|
||||||
else:
|
else:
|
||||||
@ -61,12 +66,14 @@ def get_fifo_queue(filters):
|
|||||||
batch[0] -= qty_to_pop
|
batch[0] -= qty_to_pop
|
||||||
qty_to_pop = 0
|
qty_to_pop = 0
|
||||||
|
|
||||||
|
prev_qty = d.qty_after_transaction
|
||||||
|
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
def get_stock_ledger_entries(filters):
|
def get_stock_ledger_entries(filters):
|
||||||
return frappe.db.sql("""select
|
return frappe.db.sql("""select
|
||||||
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
||||||
actual_qty, posting_date
|
actual_qty, posting_date, voucher_type, qty_after_transaction
|
||||||
from `tabStock Ledger Entry` sle,
|
from `tabStock Ledger Entry` sle,
|
||||||
(select name, item_name, description, stock_uom, brand, item_group
|
(select name, item_name, description, stock_uom, brand, item_group
|
||||||
from `tabItem` {item_conditions}) item
|
from `tabItem` {item_conditions}) item
|
||||||
|
@ -13,16 +13,13 @@ def execute(filters=None):
|
|||||||
data = []
|
data = []
|
||||||
for sle in sl_entries:
|
for sle in sl_entries:
|
||||||
item_detail = item_details[sle.item_code]
|
item_detail = item_details[sle.item_code]
|
||||||
voucher_link_icon = """<a href="%s"><i class="icon icon-share"
|
|
||||||
style="cursor: pointer;"></i></a>""" \
|
|
||||||
% ("/".join(["#Form", sle.voucher_type, sle.voucher_no]),)
|
|
||||||
|
|
||||||
data.append([sle.date, sle.item_code, item_detail.item_name, item_detail.item_group,
|
data.append([sle.date, sle.item_code, item_detail.item_name, item_detail.item_group,
|
||||||
item_detail.brand, item_detail.description, sle.warehouse,
|
item_detail.brand, item_detail.description, sle.warehouse,
|
||||||
item_detail.stock_uom, sle.actual_qty, sle.qty_after_transaction,
|
item_detail.stock_uom, sle.actual_qty, sle.qty_after_transaction,
|
||||||
(sle.incoming_rate if sle.actual_qty > 0 else 0.0),
|
(sle.incoming_rate if sle.actual_qty > 0 else 0.0),
|
||||||
sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no,
|
sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no,
|
||||||
voucher_link_icon, sle.batch_no, sle.serial_no, sle.company])
|
sle.batch_no, sle.serial_no, sle.company])
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
@ -31,7 +28,7 @@ def get_columns():
|
|||||||
_("Brand") + ":Link/Brand:100", _("Description") + "::200", _("Warehouse") + ":Link/Warehouse:100",
|
_("Brand") + ":Link/Brand:100", _("Description") + "::200", _("Warehouse") + ":Link/Warehouse:100",
|
||||||
_("Stock UOM") + ":Link/UOM:100", _("Qty") + ":Float:50", _("Balance Qty") + ":Float:100",
|
_("Stock UOM") + ":Link/UOM:100", _("Qty") + ":Float:50", _("Balance Qty") + ":Float:100",
|
||||||
_("Incoming Rate") + ":Currency:110", _("Valuation Rate") + ":Currency:110", _("Balance Value") + ":Currency:110",
|
_("Incoming Rate") + ":Currency:110", _("Valuation Rate") + ":Currency:110", _("Balance Value") + ":Currency:110",
|
||||||
_("Voucher Type") + "::110", _("Voucher #") + "::100", _("Link") + "::30", _("Batch") + ":Link/Batch:100",
|
_("Voucher Type") + "::110", _("Voucher #") + ":Dynamic Link/Voucher Type:100", _("Batch") + ":Link/Batch:100",
|
||||||
_("Serial #") + ":Link/Serial No:100", _("Company") + ":Link/Company:100"]
|
_("Serial #") + ":Link/Serial No:100", _("Company") + ":Link/Company:100"]
|
||||||
|
|
||||||
def get_stock_ledger_entries(filters):
|
def get_stock_ledger_entries(filters):
|
||||||
|
@ -27,7 +27,7 @@ def make_sl_entries(sl_entries, is_amended=None):
|
|||||||
if sle.get('is_cancelled') == 'Yes':
|
if sle.get('is_cancelled') == 'Yes':
|
||||||
sle['actual_qty'] = -flt(sle['actual_qty'])
|
sle['actual_qty'] = -flt(sle['actual_qty'])
|
||||||
|
|
||||||
if sle.get("actual_qty"):
|
if sle.get("actual_qty") or sle.voucher_type=="Stock Reconciliation":
|
||||||
sle_id = make_entry(sle)
|
sle_id = make_entry(sle)
|
||||||
|
|
||||||
args = sle.copy()
|
args = sle.copy()
|
||||||
@ -36,9 +36,9 @@ def make_sl_entries(sl_entries, is_amended=None):
|
|||||||
"is_amended": is_amended
|
"is_amended": is_amended
|
||||||
})
|
})
|
||||||
update_bin(args)
|
update_bin(args)
|
||||||
|
|
||||||
if cancel:
|
if cancel:
|
||||||
delete_cancelled_entry(sl_entries[0].get('voucher_type'),
|
delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
|
||||||
sl_entries[0].get('voucher_no'))
|
|
||||||
|
|
||||||
def set_as_cancel(voucher_type, voucher_no):
|
def set_as_cancel(voucher_type, voucher_no):
|
||||||
frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
|
frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
|
||||||
@ -83,7 +83,6 @@ def update_entries_after(args, verbose=1):
|
|||||||
|
|
||||||
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
||||||
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
||||||
|
|
||||||
valuation_method = get_valuation_method(args["item_code"])
|
valuation_method = get_valuation_method(args["item_code"])
|
||||||
stock_value_difference = 0.0
|
stock_value_difference = 0.0
|
||||||
|
|
||||||
@ -95,9 +94,18 @@ def update_entries_after(args, verbose=1):
|
|||||||
qty_after_transaction += flt(sle.actual_qty)
|
qty_after_transaction += flt(sle.actual_qty)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
if sle.serial_no:
|
if sle.serial_no:
|
||||||
valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
|
valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
|
||||||
elif valuation_method == "Moving Average":
|
qty_after_transaction += flt(sle.actual_qty)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if sle.voucher_type=="Stock Reconciliation":
|
||||||
|
valuation_rate = sle.valuation_rate
|
||||||
|
qty_after_transaction = sle.qty_after_transaction
|
||||||
|
stock_queue = [[qty_after_transaction, valuation_rate]]
|
||||||
|
else:
|
||||||
|
if valuation_method == "Moving Average":
|
||||||
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate)
|
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate)
|
||||||
else:
|
else:
|
||||||
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue)
|
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue)
|
||||||
|
@ -209,3 +209,30 @@ def reset_serial_no_status_and_warehouse(serial_nos=None):
|
|||||||
|
|
||||||
frappe.db.sql("""update `tabSerial No` set warehouse='' where status in ('Delivered', 'Purchase Returned')""")
|
frappe.db.sql("""update `tabSerial No` set warehouse='' where status in ('Delivered', 'Purchase Returned')""")
|
||||||
|
|
||||||
|
def repost_all_stock_vouchers():
|
||||||
|
vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no
|
||||||
|
from `tabStock Ledger Entry` order by posting_date, posting_time, name""")
|
||||||
|
|
||||||
|
rejected = []
|
||||||
|
i = 0
|
||||||
|
for voucher_type, voucher_no in vouchers:
|
||||||
|
i += 1
|
||||||
|
print voucher_type, voucher_no
|
||||||
|
try:
|
||||||
|
for dt in ["Stock Ledger Entry", "GL Entry"]:
|
||||||
|
frappe.db.sql("""delete from `tab%s` where voucher_type=%s and voucher_no=%s"""%
|
||||||
|
(dt, '%s', '%s'), (voucher_type, voucher_no))
|
||||||
|
|
||||||
|
doc = frappe.get_doc(voucher_type, voucher_no)
|
||||||
|
if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]:
|
||||||
|
doc.get_stock_and_rate(force=1)
|
||||||
|
doc.update_stock_ledger()
|
||||||
|
doc.make_gl_entries()
|
||||||
|
if i%100 == 0:
|
||||||
|
frappe.db.commit()
|
||||||
|
except:
|
||||||
|
rejected.append([voucher_type, voucher_no])
|
||||||
|
pass
|
||||||
|
|
||||||
|
print rejected
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user