fix: Reverse GL entry on cancellation of document

This commit is contained in:
deepeshgarg007
2019-08-05 16:45:48 +05:30
parent c6857e562b
commit 7f47b69ed7
10 changed files with 186 additions and 134 deletions

View File

@@ -5,7 +5,7 @@ frappe.ui.form.on('Period Closing Voucher', {
onload: function(frm) {
if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date());
},
setup: function(frm) {
frm.set_query("closing_account_head", function() {
return {
@@ -18,9 +18,9 @@ frappe.ui.form.on('Period Closing Voucher', {
}
});
},
refresh: function(frm) {
if(frm.doc.docstatus==1) {
if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
@@ -33,5 +33,5 @@ frappe.ui.form.on('Period Closing Voucher', {
}, "fa fa-table");
}
}
})

View File

@@ -17,8 +17,9 @@ class PeriodClosingVoucher(AccountsController):
self.make_gl_entries()
def on_cancel(self):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name)
self.flags.ignore_links = True
from erpnext.accounts.general_ledger import make_reverse_gl_entries
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name, cancel=True)
def validate_account_head(self):
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")

View File

@@ -14,7 +14,7 @@ from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, make_reverse_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
@@ -370,14 +370,14 @@ class PurchaseInvoice(BuyingController):
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock:
from erpnext.controllers.stock_controller import update_gl_entries_after
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouses, items, company = self.company)
# if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock:
# from erpnext.controllers.stock_controller import update_gl_entries_after
# items, warehouses = self.get_items_and_warehouses()
# update_gl_entries_after(self.posting_date, self.posting_time,
# warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@@ -790,6 +790,7 @@ class PurchaseInvoice(BuyingController):
frappe.db.set(self, 'status', 'Cancelled')
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
self.flags.ignore_links = True
def update_project(self):
project_list = []

View File

@@ -254,6 +254,8 @@ class SalesInvoice(SellingController):
if "Healthcare" in active_domains:
manage_invoice_submit_cancel(self, "on_cancel")
self.flags.ignore_links = True
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.append({
@@ -692,15 +694,15 @@ class SalesInvoice(SellingController):
update_outstanding_amt(self.debit_to, "Customer", self.customer,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
if repost_future_gle and cint(self.update_stock) \
and cint(auto_accounting_for_stock):
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouses, items, company = self.company)
# if repost_future_gle and cint(self.update_stock) \
# and cint(auto_accounting_for_stock):
# items, warehouses = self.get_items_and_warehouses()
# update_gl_entries_after(self.posting_date, self.posting_time,
# warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) \
and cint(auto_accounting_for_stock):
from erpnext.accounts.general_ledger import delete_gl_entries
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
from erpnext.accounts.general_ledger import make_reverse_gl_entries
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries

View File

@@ -21,7 +21,7 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd
else:
frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."))
else:
delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding, cancel=cancel)
def process_gl_map(gl_map, merge_entries=True):
if merge_entries:
@@ -196,26 +196,72 @@ def get_round_off_account_and_cost_center(company):
return round_off_account, round_off_cost_center
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"):
# def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
# adv_adj=False, update_outstanding="Yes"):
# from erpnext.accounts.doctype.gl_entry.gl_entry import validate_balance_type, \
# check_freezing_date, update_outstanding_amt, validate_frozen_account
# if not gl_entries:
# gl_entries = frappe.db.sql("""
# select account, posting_date, party_type, party, cost_center, fiscal_year,voucher_type,
# voucher_no, against_voucher_type, against_voucher, cost_center, company
# from `tabGL Entry`
# where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
# if gl_entries:
# check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
# frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
# (voucher_type or gl_entries[0]["voucher_type"], voucher_no or gl_entries[0]["voucher_no"]))
# for entry in gl_entries:
# validate_frozen_account(entry["account"], adv_adj)
# validate_balance_type(entry["account"], adv_adj)
# if not adv_adj:
# validate_expense_against_budget(entry)
# if entry.get("against_voucher") and update_outstanding == 'Yes' and not adv_adj:
# update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
# entry.get("against_voucher"), on_cancel=True)
def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes", cancel=False):
from erpnext.accounts.doctype.gl_entry.gl_entry import validate_balance_type, \
check_freezing_date, update_outstanding_amt, validate_frozen_account
if not gl_entries:
gl_entries = frappe.db.sql("""
select account, posting_date, party_type, party, cost_center, fiscal_year,voucher_type,
voucher_no, against_voucher_type, against_voucher, cost_center, company
from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
gl_entries = frappe.get_all("GL Entry",
fields = ["*"],
filters = {
"voucher_type": voucher_type,
"voucher_no": voucher_no
})
if gl_entries:
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
(voucher_type or gl_entries[0]["voucher_type"], voucher_no or gl_entries[0]["voucher_no"]))
for entry in gl_entries:
debit = entry.get('debit', 0)
credit = entry.get('credit', 0)
debit_in_account_currency = entry.get('debit_in_account_currency', 0)
credit_in_account_currency = entry.get('credit_in_account_currency', 0)
entry['debit'] = credit
entry['credit'] = debit
entry['debit_in_account_currency'] = debit_in_account_currency
entry['credit_in_account_currency'] = credit_in_account_currency
if cancel:
entry['remarks'] = "On cancellation of " + entry['voucher_no']
if entry['debit'] or entry['credit']:
make_entry(entry, adv_adj, "No")
validate_frozen_account(entry["account"], adv_adj)
validate_balance_type(entry["account"], adv_adj)
if not adv_adj:

View File

@@ -11,7 +11,7 @@ from frappe.model.document import Document
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_depreciation_accounts
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
from erpnext.accounts.utils import get_account_currency
from erpnext.controllers.accounts_controller import AccountsController
@@ -42,7 +42,8 @@ class Asset(AccountsController):
self.validate_cancellation()
self.delete_depreciation_entries()
self.set_status()
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
self.flags.ignore_links = True
make_reverse_gl_entries(voucher_type='Asset', voucher_no=self.name, cancel=True)
self.db_set('booked_fixed_asset', 0)
def validate_item(self):

View File

@@ -7,7 +7,7 @@ from frappe.utils import cint, flt, cstr
from frappe import _
import frappe.defaults
from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
@@ -23,7 +23,7 @@ class StockController(AccountsController):
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
if self.docstatus == 2:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(erpnext.is_perpetual_inventory_enabled(self.company)):
warehouse_account = get_warehouse_account_map(self.company)
@@ -33,10 +33,10 @@ class StockController(AccountsController):
gl_entries = self.get_gl_entries(warehouse_account)
make_gl_entries(gl_entries, from_repost=from_repost)
if repost_future_gle:
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
warehouse_account, company=self.company)
# if repost_future_gle:
# items, warehouses = self.get_items_and_warehouses()
# update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
# warehouse_account, company=self.company)
elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1:
gl_entries = []
gl_entries = self.get_asset_gl_entry(gl_entries)
@@ -374,75 +374,75 @@ class StockController(AccountsController):
for blanket_order in blanket_orders:
frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty()
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None, company=None):
def _delete_gl_entries(voucher_type, voucher_no):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
# def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
# warehouse_account=None, company=None):
# def _delete_gl_entries(voucher_type, voucher_no):
# frappe.db.sql("""delete from `tabGL Entry`
# where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)
# if not warehouse_account:
# warehouse_account = get_warehouse_account_map(company)
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
# future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
# gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
for voucher_type, voucher_no in future_stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
if expected_gle:
if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
_delete_gl_entries(voucher_type, voucher_no)
voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
else:
_delete_gl_entries(voucher_type, voucher_no)
# for voucher_type, voucher_no in future_stock_vouchers:
# existing_gle = gle.get((voucher_type, voucher_no), [])
# voucher_obj = frappe.get_doc(voucher_type, voucher_no)
# expected_gle = voucher_obj.get_gl_entries(warehouse_account)
# if expected_gle:
# if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
# _delete_gl_entries(voucher_type, voucher_no)
# voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
# else:
# _delete_gl_entries(voucher_type, voucher_no)
def compare_existing_and_expected_gle(existing_gle, expected_gle):
matched = True
for entry in expected_gle:
account_existed = False
for e in existing_gle:
if entry.account == e.account:
account_existed = True
if entry.account == e.account and entry.against_account == e.against_account \
and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
and (entry.debit != e.debit or entry.credit != e.credit):
matched = False
break
if not account_existed:
matched = False
break
return matched
# def compare_existing_and_expected_gle(existing_gle, expected_gle):
# matched = True
# for entry in expected_gle:
# account_existed = False
# for e in existing_gle:
# if entry.account == e.account:
# account_existed = True
# if entry.account == e.account and entry.against_account == e.against_account \
# and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
# and (entry.debit != e.debit or entry.credit != e.credit):
# matched = False
# break
# if not account_existed:
# matched = False
# break
# return matched
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
future_stock_vouchers = []
# def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
# future_stock_vouchers = []
values = []
condition = ""
if for_items:
condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
values += for_items
# values = []
# condition = ""
# if for_items:
# condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
# values += for_items
if for_warehouses:
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
values += for_warehouses
# if for_warehouses:
# condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
# values += for_warehouses
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc""".format(condition=condition),
tuple([posting_date, posting_time] + values), as_dict=True):
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
# for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
# from `tabStock Ledger Entry` sle
# where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
# order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc""".format(condition=condition),
# tuple([posting_date, posting_time] + values), as_dict=True):
# future_stock_vouchers.append([d.voucher_type, d.voucher_no])
return future_stock_vouchers
# return future_stock_vouchers
def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
gl_entries = {}
if future_stock_vouchers:
for d in frappe.db.sql("""select * from `tabGL Entry`
where posting_date >= %s and voucher_no in (%s)""" %
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
# def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
# gl_entries = {}
# if future_stock_vouchers:
# for d in frappe.db.sql("""select * from `tabGL Entry`
# where posting_date >= %s and voucher_no in (%s)""" %
# ('%s', ', '.join(['%s']*len(future_stock_vouchers))),
# tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
# gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries
# return gl_entries

View File

@@ -10,7 +10,7 @@ from frappe.utils import money_in_words
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from frappe.utils.csvutils import getlink
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import delete_gl_entries
from erpnext.accounts.general_ledger import make_reverse_gl_entries
class Fees(AccountsController):
@@ -80,7 +80,8 @@ class Fees(AccountsController):
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
def on_cancel(self):
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
self.flags.ignore_links = True
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name, cancel=True)
# frappe.db.set(self, 'status', 'Cancelled')

View File

@@ -66,7 +66,7 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({
show_general_ledger: function() {
var me = this;
if(this.frm.doc.docstatus===1) {
if(this.frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: me.frm.doc.name,

View File

@@ -227,39 +227,39 @@ def reset_serial_no_status_and_warehouse(serial_nos=None):
except:
pass
def repost_all_stock_vouchers():
warehouses_with_account = frappe.db.sql_list("""select warehouse from tabAccount
where ifnull(account_type, '') = 'Stock' and (warehouse is not null and warehouse != '')
and is_group=0""")
# def repost_all_stock_vouchers():
# warehouses_with_account = frappe.db.sql_list("""select warehouse from tabAccount
# where ifnull(account_type, '') = 'Stock' and (warehouse is not null and warehouse != '')
# and is_group=0""")
vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no
from `tabStock Ledger Entry` sle
where voucher_type != "Serial No" and sle.warehouse in (%s)
order by posting_date, posting_time, creation""" %
', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account))
# vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no
# from `tabStock Ledger Entry` sle
# where voucher_type != "Serial No" and sle.warehouse in (%s)
# order by posting_date, posting_time, creation""" %
# ', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account))
rejected = []
i = 0
for voucher_type, voucher_no in vouchers:
i+=1
print(i, "/", len(vouchers), 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))
# rejected = []
# i = 0
# for voucher_type, voucher_no in vouchers:
# i+=1
# print(i, "/", len(vouchers), 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.calculate_rate_and_amount(force=1)
elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
doc.validate()
# doc = frappe.get_doc(voucher_type, voucher_no)
# if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]:
# doc.calculate_rate_and_amount(force=1)
# elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
# doc.validate()
doc.update_stock_ledger()
doc.make_gl_entries(repost_future_gle=False)
frappe.db.commit()
except Exception:
print(frappe.get_traceback())
rejected.append([voucher_type, voucher_no])
frappe.db.rollback()
# doc.update_stock_ledger()
# doc.make_gl_entries(repost_future_gle=False)
# frappe.db.commit()
# except Exception:
# print(frappe.get_traceback())
# rejected.append([voucher_type, voucher_no])
# frappe.db.rollback()
print(rejected)
# print(rejected)