mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-16 11:39:18 +00:00
Merge pull request #38145 from frappe/version-15-hotfix
chore: v15 Release
This commit is contained in:
@@ -1022,6 +1022,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.add_bank_gl_entries(gl_entries)
|
self.add_bank_gl_entries(gl_entries)
|
||||||
self.add_deductions_gl_entries(gl_entries)
|
self.add_deductions_gl_entries(gl_entries)
|
||||||
self.add_tax_gl_entries(gl_entries)
|
self.add_tax_gl_entries(gl_entries)
|
||||||
|
add_regional_gl_entries(gl_entries, self)
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||||
@@ -1054,9 +1055,9 @@ class PaymentEntry(AccountsController):
|
|||||||
item=self,
|
item=self,
|
||||||
)
|
)
|
||||||
|
|
||||||
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
|
||||||
|
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
|
# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
|
||||||
|
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
||||||
cost_center = self.cost_center
|
cost_center = self.cost_center
|
||||||
if d.reference_doctype == "Sales Invoice" and not cost_center:
|
if d.reference_doctype == "Sales Invoice" and not cost_center:
|
||||||
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
|
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
|
||||||
@@ -1072,11 +1073,9 @@ class PaymentEntry(AccountsController):
|
|||||||
against_voucher_type = d.reference_doctype
|
against_voucher_type = d.reference_doctype
|
||||||
against_voucher = d.reference_name
|
against_voucher = d.reference_name
|
||||||
|
|
||||||
reverse_dr_or_cr, standalone_note = 0, 0
|
reverse_dr_or_cr = 0
|
||||||
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
|
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
is_return, return_against = frappe.db.get_value(
|
is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
|
||||||
d.reference_doctype, d.reference_name, ["is_return", "return_against"]
|
|
||||||
)
|
|
||||||
payable_party_types = get_party_types_from_account_type("Payable")
|
payable_party_types = get_party_types_from_account_type("Payable")
|
||||||
receivable_party_types = get_party_types_from_account_type("Receivable")
|
receivable_party_types = get_party_types_from_account_type("Receivable")
|
||||||
if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
|
if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
|
||||||
@@ -1086,7 +1085,7 @@ class PaymentEntry(AccountsController):
|
|||||||
):
|
):
|
||||||
reverse_dr_or_cr = 1
|
reverse_dr_or_cr = 1
|
||||||
|
|
||||||
if is_return and not return_against and not reverse_dr_or_cr:
|
if is_return and not reverse_dr_or_cr:
|
||||||
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
||||||
|
|
||||||
gle.update(
|
gle.update(
|
||||||
@@ -1100,6 +1099,7 @@ class PaymentEntry(AccountsController):
|
|||||||
)
|
)
|
||||||
gl_entries.append(gle)
|
gl_entries.append(gle)
|
||||||
|
|
||||||
|
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
||||||
if self.unallocated_amount:
|
if self.unallocated_amount:
|
||||||
exchange_rate = self.get_exchange_rate()
|
exchange_rate = self.get_exchange_rate()
|
||||||
base_unallocated_amount = self.unallocated_amount * exchange_rate
|
base_unallocated_amount = self.unallocated_amount * exchange_rate
|
||||||
@@ -2611,3 +2611,8 @@ def make_payment_order(source_name, target_doc=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
|
|
||||||
|
@erpnext.allow_regional
|
||||||
|
def add_regional_gl_entries(gl_entries, doc):
|
||||||
|
return
|
||||||
|
|||||||
@@ -1250,6 +1250,9 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
self.assertEqual(so.advance_paid, so.rounded_total)
|
self.assertEqual(so.advance_paid, so.rounded_total)
|
||||||
|
|
||||||
def test_receive_payment_from_payable_party_type(self):
|
def test_receive_payment_from_payable_party_type(self):
|
||||||
|
"""
|
||||||
|
Checks GL entries generated while receiving payments from a Payable Party Type.
|
||||||
|
"""
|
||||||
pe = create_payment_entry(
|
pe = create_payment_entry(
|
||||||
party_type="Supplier",
|
party_type="Supplier",
|
||||||
party="_Test Supplier",
|
party="_Test Supplier",
|
||||||
@@ -1261,8 +1264,57 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
self.voucher_no = pe.name
|
self.voucher_no = pe.name
|
||||||
self.expected_gle = [
|
self.expected_gle = [
|
||||||
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
|
|
||||||
{"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0},
|
{"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0},
|
||||||
|
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
|
||||||
|
]
|
||||||
|
self.check_gl_entries()
|
||||||
|
|
||||||
|
def test_payment_against_partial_return_invoice(self):
|
||||||
|
"""
|
||||||
|
Checks GL entries generated for partial return invoice payments.
|
||||||
|
"""
|
||||||
|
si = create_sales_invoice(qty=10, rate=10, customer="_Test Customer")
|
||||||
|
credit_note = create_sales_invoice(
|
||||||
|
qty=-4, rate=10, customer="_Test Customer", is_return=1, return_against=si.name
|
||||||
|
)
|
||||||
|
pe = create_payment_entry(
|
||||||
|
party_type="Customer",
|
||||||
|
party="_Test Customer",
|
||||||
|
payment_type="Receive",
|
||||||
|
paid_from="Debtors - _TC",
|
||||||
|
paid_to="_Test Cash - _TC",
|
||||||
|
)
|
||||||
|
pe.set(
|
||||||
|
"references",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"reference_doctype": "Sales Invoice",
|
||||||
|
"reference_name": si.name,
|
||||||
|
"due_date": si.get("due_date"),
|
||||||
|
"total_amount": si.grand_total,
|
||||||
|
"outstanding_amount": si.outstanding_amount,
|
||||||
|
"allocated_amount": si.outstanding_amount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference_doctype": "Sales Invoice",
|
||||||
|
"reference_name": credit_note.name,
|
||||||
|
"due_date": credit_note.get("due_date"),
|
||||||
|
"total_amount": credit_note.grand_total,
|
||||||
|
"outstanding_amount": credit_note.outstanding_amount,
|
||||||
|
"allocated_amount": credit_note.outstanding_amount,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
pe.save()
|
||||||
|
pe.submit()
|
||||||
|
self.assertEqual(pe.total_allocated_amount, 60)
|
||||||
|
self.assertEqual(pe.unallocated_amount, 940)
|
||||||
|
self.voucher_no = pe.name
|
||||||
|
self.expected_gle = [
|
||||||
|
{"account": "Debtors - _TC", "debit": 40.0, "credit": 0.0},
|
||||||
|
{"account": "Debtors - _TC", "debit": 0.0, "credit": 940.0},
|
||||||
|
{"account": "Debtors - _TC", "debit": 0.0, "credit": 100.0},
|
||||||
|
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
|
||||||
]
|
]
|
||||||
self.check_gl_entries()
|
self.check_gl_entries()
|
||||||
|
|
||||||
@@ -1276,7 +1328,7 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
gle.credit,
|
gle.credit,
|
||||||
)
|
)
|
||||||
.where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0))
|
.where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0))
|
||||||
.orderby(gle.account)
|
.orderby(gle.account, gle.debit, gle.credit, order=frappe.qb.desc)
|
||||||
).run(as_dict=True)
|
).run(as_dict=True)
|
||||||
for row in range(len(self.expected_gle)):
|
for row in range(len(self.expected_gle)):
|
||||||
for field in ["account", "debit", "credit"]:
|
for field in ["account", "debit", "credit"]:
|
||||||
|
|||||||
@@ -186,6 +186,7 @@
|
|||||||
"label": "Image"
|
"label": "Image"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -833,7 +834,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-03-12 13:36:40.160468",
|
"modified": "2023-11-14 18:33:22.585715",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Invoice Item",
|
"name": "POS Invoice Item",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from erpnext.accounts.general_ledger import (
|
|||||||
)
|
)
|
||||||
from erpnext.accounts.party import get_due_date, get_party_account
|
from erpnext.accounts.party import get_due_date, get_party_account
|
||||||
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
||||||
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
|
from erpnext.assets.doctype.asset.asset import is_cwip_accounting_enabled
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||||
from erpnext.buying.utils import check_on_hold_or_closed_status
|
from erpnext.buying.utils import check_on_hold_or_closed_status
|
||||||
from erpnext.controllers.accounts_controller import validate_account_head
|
from erpnext.controllers.accounts_controller import validate_account_head
|
||||||
@@ -281,9 +281,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
# in case of auto inventory accounting,
|
# in case of auto inventory accounting,
|
||||||
# expense account is always "Stock Received But Not Billed" for a stock item
|
# expense account is always "Stock Received But Not Billed" for a stock item
|
||||||
# except opening entry, drop-ship entry and fixed asset items
|
# except opening entry, drop-ship entry and fixed asset items
|
||||||
if item.item_code:
|
|
||||||
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
auto_accounting_for_stock
|
auto_accounting_for_stock
|
||||||
and item.item_code in stock_items
|
and item.item_code in stock_items
|
||||||
@@ -350,22 +347,26 @@ class PurchaseInvoice(BuyingController):
|
|||||||
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
||||||
|
|
||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
|
elif item.is_fixed_asset and item.pr_detail:
|
||||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
if not asset_received_but_not_billed:
|
||||||
|
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
|
||||||
|
item.expense_account = asset_received_but_not_billed
|
||||||
|
elif item.is_fixed_asset:
|
||||||
|
account_type = (
|
||||||
|
"capital_work_in_progress_account"
|
||||||
|
if is_cwip_accounting_enabled(item.asset_category)
|
||||||
|
else "fixed_asset_account"
|
||||||
|
)
|
||||||
asset_category_account = get_asset_category_account(
|
asset_category_account = get_asset_category_account(
|
||||||
"fixed_asset_account", item=item.item_code, company=self.company
|
account_type, item=item.item_code, company=self.company
|
||||||
)
|
)
|
||||||
if not asset_category_account:
|
if not asset_category_account:
|
||||||
form_link = get_link_to_form("Asset Category", asset_category)
|
form_link = get_link_to_form("Asset Category", item.asset_category)
|
||||||
throw(
|
throw(
|
||||||
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
|
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
|
||||||
title=_("Missing Account"),
|
title=_("Missing Account"),
|
||||||
)
|
)
|
||||||
item.expense_account = asset_category_account
|
item.expense_account = asset_category_account
|
||||||
elif item.is_fixed_asset and item.pr_detail:
|
|
||||||
if not asset_received_but_not_billed:
|
|
||||||
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
|
|
||||||
item.expense_account = asset_received_but_not_billed
|
|
||||||
elif not item.expense_account and for_validate:
|
elif not item.expense_account and for_validate:
|
||||||
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
||||||
|
|
||||||
@@ -584,12 +585,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
def get_gl_entries(self, warehouse_account=None):
|
def get_gl_entries(self, warehouse_account=None):
|
||||||
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
|
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
|
||||||
|
|
||||||
if self.auto_accounting_for_stock:
|
if self.auto_accounting_for_stock:
|
||||||
self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
|
self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
|
||||||
self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
|
||||||
else:
|
else:
|
||||||
self.stock_received_but_not_billed = None
|
self.stock_received_but_not_billed = None
|
||||||
self.expenses_included_in_valuation = None
|
|
||||||
|
|
||||||
self.negative_expense_to_be_booked = 0.0
|
self.negative_expense_to_be_booked = 0.0
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
@@ -598,9 +598,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.make_item_gl_entries(gl_entries)
|
self.make_item_gl_entries(gl_entries)
|
||||||
self.make_precision_loss_gl_entry(gl_entries)
|
self.make_precision_loss_gl_entry(gl_entries)
|
||||||
|
|
||||||
if self.check_asset_cwip_enabled():
|
|
||||||
self.get_asset_gl_entry(gl_entries)
|
|
||||||
|
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
self.make_internal_transfer_gl_entries(gl_entries)
|
self.make_internal_transfer_gl_entries(gl_entries)
|
||||||
|
|
||||||
@@ -702,7 +699,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if item.item_code:
|
if item.item_code:
|
||||||
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||||
|
|
||||||
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
|
if (
|
||||||
|
self.update_stock
|
||||||
|
and self.auto_accounting_for_stock
|
||||||
|
and (item.item_code in stock_items or item.is_fixed_asset)
|
||||||
|
):
|
||||||
# warehouse account
|
# warehouse account
|
||||||
warehouse_debit_amount = self.make_stock_adjustment_entry(
|
warehouse_debit_amount = self.make_stock_adjustment_entry(
|
||||||
gl_entries, item, voucher_wise_stock_value, account_currency
|
gl_entries, item, voucher_wise_stock_value, account_currency
|
||||||
@@ -817,9 +818,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif not item.is_fixed_asset or (
|
else:
|
||||||
item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)
|
|
||||||
):
|
|
||||||
expense_account = (
|
expense_account = (
|
||||||
item.expense_account
|
item.expense_account
|
||||||
if (not item.enable_deferred_expense or self.is_return)
|
if (not item.enable_deferred_expense or self.is_return)
|
||||||
@@ -912,40 +911,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# If asset is bought through this document and not linked to PR
|
|
||||||
if self.update_stock and item.landed_cost_voucher_amount:
|
|
||||||
expenses_included_in_asset_valuation = self.get_company_default(
|
|
||||||
"expenses_included_in_asset_valuation"
|
|
||||||
)
|
|
||||||
# Amount added through landed-cost-voucher
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": expenses_included_in_asset_valuation,
|
|
||||||
"against": expense_account,
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
|
||||||
"credit": flt(item.landed_cost_voucher_amount),
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": expense_account,
|
|
||||||
"against": expenses_included_in_asset_valuation,
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
|
||||||
"debit": flt(item.landed_cost_voucher_amount),
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# update gross amount of asset bought through this document
|
# update gross amount of asset bought through this document
|
||||||
assets = frappe.db.get_all(
|
assets = frappe.db.get_all(
|
||||||
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
||||||
@@ -970,11 +935,17 @@ class PurchaseInvoice(BuyingController):
|
|||||||
(item.purchase_receipt, valuation_tax_accounts),
|
(item.purchase_receipt, valuation_tax_accounts),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stock_rbnb = (
|
||||||
|
self.get_company_default("asset_received_but_not_billed")
|
||||||
|
if item.is_fixed_asset
|
||||||
|
else self.stock_received_but_not_billed
|
||||||
|
)
|
||||||
|
|
||||||
if not negative_expense_booked_in_pr:
|
if not negative_expense_booked_in_pr:
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
{
|
{
|
||||||
"account": self.stock_received_but_not_billed,
|
"account": stock_rbnb,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
|
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
|
||||||
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
||||||
@@ -989,156 +960,12 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount, item.precision("item_tax_amount")
|
item.item_tax_amount, item.precision("item_tax_amount")
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_asset_gl_entry(self, gl_entries):
|
assets = frappe.db.get_all(
|
||||||
arbnb_account = None
|
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
||||||
eiiav_account = None
|
)
|
||||||
asset_eiiav_currency = None
|
for asset in assets:
|
||||||
|
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||||
for item in self.get("items"):
|
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
|
||||||
if item.is_fixed_asset:
|
|
||||||
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate)
|
|
||||||
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
|
||||||
|
|
||||||
item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type")
|
|
||||||
if not item.expense_account or item_exp_acc_type not in [
|
|
||||||
"Asset Received But Not Billed",
|
|
||||||
"Fixed Asset",
|
|
||||||
]:
|
|
||||||
if not arbnb_account:
|
|
||||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
|
||||||
item.expense_account = arbnb_account
|
|
||||||
|
|
||||||
if not self.update_stock:
|
|
||||||
arbnb_currency = get_account_currency(item.expense_account)
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": item.expense_account,
|
|
||||||
"against": self.supplier,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
|
||||||
"debit": base_asset_amount,
|
|
||||||
"debit_in_account_currency": (
|
|
||||||
base_asset_amount if arbnb_currency == self.company_currency else asset_amount
|
|
||||||
),
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if item.item_tax_amount:
|
|
||||||
if not eiiav_account or not asset_eiiav_currency:
|
|
||||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
|
||||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
|
||||||
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": eiiav_account,
|
|
||||||
"against": self.supplier,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"project": item.project or self.project,
|
|
||||||
"credit": item.item_tax_amount,
|
|
||||||
"credit_in_account_currency": (
|
|
||||||
item.item_tax_amount
|
|
||||||
if asset_eiiav_currency == self.company_currency
|
|
||||||
else item.item_tax_amount / self.conversion_rate
|
|
||||||
),
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
cwip_account = get_asset_account(
|
|
||||||
"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
|
|
||||||
)
|
|
||||||
|
|
||||||
cwip_account_currency = get_account_currency(cwip_account)
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": cwip_account,
|
|
||||||
"against": self.supplier,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
|
||||||
"debit": base_asset_amount,
|
|
||||||
"debit_in_account_currency": (
|
|
||||||
base_asset_amount if cwip_account_currency == self.company_currency else asset_amount
|
|
||||||
),
|
|
||||||
"cost_center": item.cost_center or self.cost_center,
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
|
||||||
if not eiiav_account or not asset_eiiav_currency:
|
|
||||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
|
||||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
|
||||||
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": eiiav_account,
|
|
||||||
"against": self.supplier,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"credit": item.item_tax_amount,
|
|
||||||
"project": item.project or self.project,
|
|
||||||
"credit_in_account_currency": (
|
|
||||||
item.item_tax_amount
|
|
||||||
if asset_eiiav_currency == self.company_currency
|
|
||||||
else item.item_tax_amount / self.conversion_rate
|
|
||||||
),
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Assets are bought through this document then it will be linked to this document
|
|
||||||
if flt(item.landed_cost_voucher_amount):
|
|
||||||
if not eiiav_account:
|
|
||||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
|
||||||
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": eiiav_account,
|
|
||||||
"against": cwip_account,
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
|
||||||
"credit": flt(item.landed_cost_voucher_amount),
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
gl_entries.append(
|
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": cwip_account,
|
|
||||||
"against": eiiav_account,
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
|
||||||
"debit": flt(item.landed_cost_voucher_amount),
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# update gross amount of assets bought through this document
|
|
||||||
assets = frappe.db.get_all(
|
|
||||||
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
|
||||||
)
|
|
||||||
for asset in assets:
|
|
||||||
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
|
||||||
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
|
|
||||||
|
|
||||||
return gl_entries
|
|
||||||
|
|
||||||
def make_stock_adjustment_entry(
|
def make_stock_adjustment_entry(
|
||||||
self, gl_entries, item, voucher_wise_stock_value, account_currency
|
self, gl_entries, item, voucher_wise_stock_value, account_currency
|
||||||
|
|||||||
@@ -158,6 +158,7 @@
|
|||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -915,7 +916,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-03 21:01:01.824892",
|
"modified": "2023-11-14 18:33:48.547297",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
|||||||
@@ -167,6 +167,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -901,7 +902,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-26 12:53:22.404057",
|
"modified": "2023-11-14 18:34:10.479329",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
@@ -911,4 +912,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -144,6 +144,11 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"label": __("Show Future Payments"),
|
"label": __("Show Future Payments"),
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "for_revaluation_journals",
|
||||||
|
"label": __("Revaluation Journals"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "ignore_accounts",
|
"fieldname": "ignore_accounts",
|
||||||
"label": __("Group by Voucher"),
|
"label": __("Group by Voucher"),
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ GL_REPOSTING_CHUNK = 100
|
|||||||
def get_fiscal_year(
|
def get_fiscal_year(
|
||||||
date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False
|
date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False
|
||||||
):
|
):
|
||||||
|
if isinstance(boolean, str):
|
||||||
|
boolean = frappe.json.loads(boolean)
|
||||||
|
|
||||||
fiscal_years = get_fiscal_years(
|
fiscal_years = get_fiscal_years(
|
||||||
date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean
|
date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -481,11 +481,10 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval.doc.asset_quantity",
|
|
||||||
"fieldname": "asset_quantity",
|
"fieldname": "asset_quantity",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"label": "Asset Quantity",
|
"label": "Asset Quantity",
|
||||||
"read_only": 1
|
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "depr_entry_posting_status",
|
"fieldname": "depr_entry_posting_status",
|
||||||
@@ -572,7 +571,7 @@
|
|||||||
"link_fieldname": "target_asset"
|
"link_fieldname": "target_asset"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2023-10-27 17:03:46.629617",
|
"modified": "2023-11-15 17:40:17.315203",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset",
|
"name": "Asset",
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ from frappe.utils import (
|
|||||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
from erpnext.assets.doctype.asset.asset import (
|
from erpnext.assets.doctype.asset.asset import (
|
||||||
get_asset_value_after_depreciation,
|
|
||||||
make_sales_invoice,
|
make_sales_invoice,
|
||||||
split_asset,
|
split_asset,
|
||||||
update_maintenance_status,
|
update_maintenance_status,
|
||||||
@@ -194,6 +193,7 @@ class TestAsset(AssetSetup):
|
|||||||
def test_is_fixed_asset_set(self):
|
def test_is_fixed_asset_set(self):
|
||||||
asset = create_asset(is_existing_asset=1)
|
asset = create_asset(is_existing_asset=1)
|
||||||
doc = frappe.new_doc("Purchase Invoice")
|
doc = frappe.new_doc("Purchase Invoice")
|
||||||
|
doc.company = "_Test Company"
|
||||||
doc.supplier = "_Test Supplier"
|
doc.supplier = "_Test Supplier"
|
||||||
doc.append("items", {"item_code": "Macbook Pro", "qty": 1, "asset": asset.name})
|
doc.append("items", {"item_code": "Macbook Pro", "qty": 1, "asset": asset.name})
|
||||||
|
|
||||||
@@ -534,7 +534,7 @@ class TestAsset(AssetSetup):
|
|||||||
|
|
||||||
self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account)
|
self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account)
|
||||||
|
|
||||||
# CWIP: Capital Work In Progress
|
# Capital Work In Progress
|
||||||
def test_cwip_accounting(self):
|
def test_cwip_accounting(self):
|
||||||
pr = make_purchase_receipt(
|
pr = make_purchase_receipt(
|
||||||
item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location"
|
item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location"
|
||||||
@@ -567,7 +567,8 @@ class TestAsset(AssetSetup):
|
|||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
expected_gle = (
|
expected_gle = (
|
||||||
("Asset Received But Not Billed - _TC", 0.0, 5250.0),
|
("_Test Account Shipping Charges - _TC", 0.0, 250.0),
|
||||||
|
("Asset Received But Not Billed - _TC", 0.0, 5000.0),
|
||||||
("CWIP Account - _TC", 5250.0, 0.0),
|
("CWIP Account - _TC", 5250.0, 0.0),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -586,9 +587,8 @@ class TestAsset(AssetSetup):
|
|||||||
expected_gle = (
|
expected_gle = (
|
||||||
("_Test Account Service Tax - _TC", 250.0, 0.0),
|
("_Test Account Service Tax - _TC", 250.0, 0.0),
|
||||||
("_Test Account Shipping Charges - _TC", 250.0, 0.0),
|
("_Test Account Shipping Charges - _TC", 250.0, 0.0),
|
||||||
("Asset Received But Not Billed - _TC", 5250.0, 0.0),
|
("Asset Received But Not Billed - _TC", 5000.0, 0.0),
|
||||||
("Creditors - _TC", 0.0, 5500.0),
|
("Creditors - _TC", 0.0, 5500.0),
|
||||||
("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
pi_gle = frappe.db.sql(
|
pi_gle = frappe.db.sql(
|
||||||
@@ -1791,6 +1791,7 @@ def create_asset_category():
|
|||||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||||
"depreciation_expense_account": "_Test Depreciations - _TC",
|
"depreciation_expense_account": "_Test Depreciations - _TC",
|
||||||
|
"capital_work_in_progress_account": "CWIP Account - _TC",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
asset_category.append(
|
asset_category.append(
|
||||||
|
|||||||
@@ -1,30 +1,21 @@
|
|||||||
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Bulk Transaction Log', {
|
frappe.ui.form.on("Bulk Transaction Log", {
|
||||||
|
refresh(frm) {
|
||||||
refresh: function(frm) {
|
frm.add_custom_button(__('Succeeded Entries'), function() {
|
||||||
frm.disable_save();
|
frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Success"});
|
||||||
frm.add_custom_button(__('Retry Failed Transactions'), ()=>{
|
}, __("View"));
|
||||||
frappe.confirm(__("Retry Failing Transactions ?"), ()=>{
|
frm.add_custom_button(__('Failed Entries'), function() {
|
||||||
query(frm, 1);
|
frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Failed"});
|
||||||
}
|
}, __("View"));
|
||||||
);
|
if (frm.doc.failed) {
|
||||||
});
|
frm.add_custom_button(__('Retry Failed Transactions'), function() {
|
||||||
}
|
frappe.call({
|
||||||
|
method: "erpnext.utilities.bulk_transaction.retry",
|
||||||
|
args: {date: frm.doc.date}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function query(frm) {
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction",
|
|
||||||
args: {
|
|
||||||
log_date: frm.doc.log_date
|
|
||||||
}
|
|
||||||
}).then((r) => {
|
|
||||||
if (r.message === "No Failed Records") {
|
|
||||||
frappe.show_alert(__(r.message), 5);
|
|
||||||
} else {
|
|
||||||
frappe.show_alert(__("Retrying Failed Transactions"), 5);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,64 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_rename": 1,
|
"allow_copy": 1,
|
||||||
"creation": "2021-11-30 13:41:16.343827",
|
"creation": "2023-11-09 20:14:45.139593",
|
||||||
|
"default_view": "List",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"log_date",
|
"date",
|
||||||
"logger_data"
|
"column_break_bsan",
|
||||||
|
"log_entries",
|
||||||
|
"section_break_mdmv",
|
||||||
|
"succeeded",
|
||||||
|
"column_break_qryp",
|
||||||
|
"failed"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "log_date",
|
"fieldname": "date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Log Date",
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Date",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "logger_data",
|
"fieldname": "log_entries",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Int",
|
||||||
"label": "Logger Data",
|
"in_list_view": 1,
|
||||||
"options": "Bulk Transaction Log Detail"
|
"label": "Log Entries",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_bsan",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_mdmv",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "succeeded",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Succeeded",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_qryp",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "failed",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Failed",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"in_create": 1,
|
||||||
|
"is_virtual": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-02-03 17:23:02.935325",
|
"modified": "2023-11-11 04:52:49.347376",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Bulk Transaction",
|
"module": "Bulk Transaction",
|
||||||
"name": "Bulk Transaction Log",
|
"name": "Bulk Transaction Log",
|
||||||
@@ -47,5 +80,5 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"title_field": "date"
|
||||||
}
|
}
|
||||||
@@ -1,67 +1,112 @@
|
|||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import qb
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder.functions import Count
|
||||||
from erpnext.utilities.bulk_transaction import task, update_logger
|
from frappe.utils import cint
|
||||||
|
from pypika import Order
|
||||||
|
|
||||||
|
|
||||||
class BulkTransactionLog(Document):
|
class BulkTransactionLog(Document):
|
||||||
pass
|
def db_insert(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def load_from_db(self):
|
||||||
|
log_detail = qb.DocType("Bulk Transaction Log Detail")
|
||||||
|
|
||||||
@frappe.whitelist()
|
has_records = frappe.db.sql(
|
||||||
def retry_failing_transaction(log_date=None):
|
f"select exists (select * from `tabBulk Transaction Log Detail` where date = '{self.name}');"
|
||||||
if not log_date:
|
)[0][0]
|
||||||
log_date = str(date.today())
|
if not has_records:
|
||||||
btp = frappe.qb.DocType("Bulk Transaction Log Detail")
|
raise frappe.DoesNotExistError
|
||||||
data = (
|
|
||||||
frappe.qb.from_(btp)
|
|
||||||
.select(btp.transaction_name, btp.from_doctype, btp.to_doctype)
|
|
||||||
.distinct()
|
|
||||||
.where(btp.retried != 1)
|
|
||||||
.where(btp.transaction_status == "Failed")
|
|
||||||
.where(btp.date == log_date)
|
|
||||||
).run(as_dict=True)
|
|
||||||
|
|
||||||
if data:
|
succeeded_logs = (
|
||||||
if len(data) > 10:
|
qb.from_(log_detail)
|
||||||
frappe.enqueue(job, queue="long", job_name="bulk_retry", data=data, log_date=log_date)
|
.select(Count(log_detail.date).as_("count"))
|
||||||
else:
|
.where((log_detail.date == self.name) & (log_detail.transaction_status == "Success"))
|
||||||
job(data, log_date)
|
.run()
|
||||||
else:
|
)[0][0] or 0
|
||||||
return "No Failed Records"
|
failed_logs = (
|
||||||
|
qb.from_(log_detail)
|
||||||
|
.select(Count(log_detail.date).as_("count"))
|
||||||
|
.where((log_detail.date == self.name) & (log_detail.transaction_status == "Failed"))
|
||||||
|
.run()
|
||||||
|
)[0][0] or 0
|
||||||
|
total_logs = succeeded_logs + failed_logs
|
||||||
|
transaction_log = frappe._dict(
|
||||||
|
{
|
||||||
|
"date": self.name,
|
||||||
|
"count": total_logs,
|
||||||
|
"succeeded": succeeded_logs,
|
||||||
|
"failed": failed_logs,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
super(Document, self).__init__(serialize_transaction_log(transaction_log))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_list(args):
|
||||||
|
filter_date = parse_list_filters(args)
|
||||||
|
limit = cint(args.get("page_length")) or 20
|
||||||
|
log_detail = qb.DocType("Bulk Transaction Log Detail")
|
||||||
|
|
||||||
def job(data, log_date):
|
dates_query = (
|
||||||
for d in data:
|
qb.from_(log_detail)
|
||||||
failed = []
|
.select(log_detail.date)
|
||||||
try:
|
.distinct()
|
||||||
frappe.db.savepoint("before_creation_of_record")
|
.orderby(log_detail.date, order=Order.desc)
|
||||||
task(d.transaction_name, d.from_doctype, d.to_doctype)
|
.limit(limit)
|
||||||
except Exception as e:
|
)
|
||||||
frappe.db.rollback(save_point="before_creation_of_record")
|
if filter_date:
|
||||||
failed.append(e)
|
dates_query = dates_query.where(log_detail.date == filter_date)
|
||||||
update_logger(
|
dates = dates_query.run()
|
||||||
d.transaction_name,
|
|
||||||
e,
|
transaction_logs = []
|
||||||
d.from_doctype,
|
if dates:
|
||||||
d.to_doctype,
|
transaction_logs_query = (
|
||||||
status="Failed",
|
qb.from_(log_detail)
|
||||||
log_date=log_date,
|
.select(log_detail.date.as_("date"), Count(log_detail.date).as_("count"))
|
||||||
restarted=1,
|
.where(log_detail.date.isin(dates))
|
||||||
|
.orderby(log_detail.date, order=Order.desc)
|
||||||
|
.groupby(log_detail.date)
|
||||||
|
.limit(limit)
|
||||||
)
|
)
|
||||||
|
transaction_logs = transaction_logs_query.run(as_dict=True)
|
||||||
|
|
||||||
if not failed:
|
return [serialize_transaction_log(x) for x in transaction_logs]
|
||||||
update_logger(
|
|
||||||
d.transaction_name,
|
@staticmethod
|
||||||
None,
|
def get_count(args):
|
||||||
d.from_doctype,
|
pass
|
||||||
d.to_doctype,
|
|
||||||
status="Success",
|
@staticmethod
|
||||||
log_date=log_date,
|
def get_stats(args):
|
||||||
restarted=1,
|
pass
|
||||||
)
|
|
||||||
|
def db_update(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_transaction_log(data):
|
||||||
|
return frappe._dict(
|
||||||
|
name=data.date,
|
||||||
|
date=data.date,
|
||||||
|
log_entries=data.count,
|
||||||
|
succeeded=data.succeeded,
|
||||||
|
failed=data.failed,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_list_filters(args):
|
||||||
|
# parse date filter
|
||||||
|
filter_date = None
|
||||||
|
for fil in args.get("filters"):
|
||||||
|
if isinstance(fil, list):
|
||||||
|
for elem in fil:
|
||||||
|
if elem == "date":
|
||||||
|
filter_date = fil[3]
|
||||||
|
return filter_date
|
||||||
|
|||||||
@@ -1,79 +1,9 @@
|
|||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
# import frappe
|
||||||
from datetime import date
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
import frappe
|
|
||||||
|
|
||||||
from erpnext.utilities.bulk_transaction import transaction_processing
|
|
||||||
|
|
||||||
|
|
||||||
class TestBulkTransactionLog(unittest.TestCase):
|
class TestBulkTransactionLog(FrappeTestCase):
|
||||||
def setUp(self):
|
pass
|
||||||
create_company()
|
|
||||||
create_customer()
|
|
||||||
create_item()
|
|
||||||
|
|
||||||
def test_entry_in_log(self):
|
|
||||||
so_name = create_so()
|
|
||||||
transaction_processing([{"name": so_name}], "Sales Order", "Sales Invoice")
|
|
||||||
doc = frappe.get_doc("Bulk Transaction Log", str(date.today()))
|
|
||||||
for d in doc.get("logger_data"):
|
|
||||||
if d.transaction_name == so_name:
|
|
||||||
self.assertEqual(d.transaction_name, so_name)
|
|
||||||
self.assertEqual(d.transaction_status, "Success")
|
|
||||||
self.assertEqual(d.from_doctype, "Sales Order")
|
|
||||||
self.assertEqual(d.to_doctype, "Sales Invoice")
|
|
||||||
self.assertEqual(d.retried, 0)
|
|
||||||
|
|
||||||
|
|
||||||
def create_company():
|
|
||||||
if not frappe.db.exists("Company", "_Test Company"):
|
|
||||||
frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Company",
|
|
||||||
"company_name": "_Test Company",
|
|
||||||
"country": "India",
|
|
||||||
"default_currency": "INR",
|
|
||||||
}
|
|
||||||
).insert()
|
|
||||||
|
|
||||||
|
|
||||||
def create_customer():
|
|
||||||
if not frappe.db.exists("Customer", "Bulk Customer"):
|
|
||||||
frappe.get_doc({"doctype": "Customer", "customer_name": "Bulk Customer"}).insert()
|
|
||||||
|
|
||||||
|
|
||||||
def create_item():
|
|
||||||
if not frappe.db.exists("Item", "MK"):
|
|
||||||
frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Item",
|
|
||||||
"item_code": "MK",
|
|
||||||
"item_name": "Milk",
|
|
||||||
"description": "Milk",
|
|
||||||
"item_group": "Products",
|
|
||||||
}
|
|
||||||
).insert()
|
|
||||||
|
|
||||||
|
|
||||||
def create_so(intent=None):
|
|
||||||
so = frappe.new_doc("Sales Order")
|
|
||||||
so.customer = "Bulk Customer"
|
|
||||||
so.company = "_Test Company"
|
|
||||||
so.transaction_date = date.today()
|
|
||||||
|
|
||||||
so.set_warehouse = "Finished Goods - _TC"
|
|
||||||
so.append(
|
|
||||||
"items",
|
|
||||||
{
|
|
||||||
"item_code": "MK",
|
|
||||||
"delivery_date": date.today(),
|
|
||||||
"qty": 10,
|
|
||||||
"rate": 80,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
so.insert()
|
|
||||||
so.submit()
|
|
||||||
return so.name
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
// frappe.ui.form.on("Bulk Transaction Log Detail", {
|
||||||
|
// refresh(frm) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
|
"from_doctype",
|
||||||
"transaction_name",
|
"transaction_name",
|
||||||
"date",
|
"date",
|
||||||
"time",
|
"time",
|
||||||
"transaction_status",
|
"transaction_status",
|
||||||
"error_description",
|
"error_description",
|
||||||
"from_doctype",
|
|
||||||
"to_doctype",
|
"to_doctype",
|
||||||
"retried"
|
"retried"
|
||||||
],
|
],
|
||||||
@@ -20,8 +20,11 @@
|
|||||||
"fieldname": "transaction_name",
|
"fieldname": "transaction_name",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"options": "from_doctype"
|
"options": "from_doctype",
|
||||||
|
"read_only": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "transaction_status",
|
"fieldname": "transaction_status",
|
||||||
@@ -39,9 +42,11 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "from_doctype",
|
"fieldname": "from_doctype",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "From Doctype",
|
"label": "From Doctype",
|
||||||
"options": "DocType",
|
"options": "DocType",
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "to_doctype",
|
"fieldname": "to_doctype",
|
||||||
@@ -54,8 +59,10 @@
|
|||||||
"fieldname": "date",
|
"fieldname": "date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Date ",
|
"label": "Date ",
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "time",
|
"fieldname": "time",
|
||||||
@@ -66,19 +73,33 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "retried",
|
"fieldname": "retried",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Retried",
|
"label": "Retried",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-02-03 19:57:31.650359",
|
"modified": "2023-11-10 11:44:10.758342",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Bulk Transaction",
|
"module": "Bulk Transaction",
|
||||||
"name": "Bulk Transaction Log Detail",
|
"name": "Bulk Transaction Log Detail",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestBulkTransactionLogDetail(FrappeTestCase):
|
||||||
|
pass
|
||||||
@@ -189,6 +189,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -916,7 +917,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-11-06 11:00:53.596417",
|
"modified": "2023-11-14 18:34:27.267382",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
|||||||
@@ -87,6 +87,7 @@
|
|||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -260,13 +261,15 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-09-24 17:26:46.276934",
|
"modified": "2023-11-14 18:34:48.327224",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Request for Quotation Item",
|
"name": "Request for Quotation Item",
|
||||||
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -133,6 +133,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -559,13 +560,15 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-19 12:36:26.913211",
|
"modified": "2023-11-14 18:35:03.435817",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier Quotation Item",
|
"name": "Supplier Quotation Item",
|
||||||
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -62,9 +62,12 @@ class StockController(AccountsController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is_asset_pr = any(d.get("is_fixed_asset") for d in self.get("items"))
|
||||||
|
|
||||||
if (
|
if (
|
||||||
cint(erpnext.is_perpetual_inventory_enabled(self.company))
|
cint(erpnext.is_perpetual_inventory_enabled(self.company))
|
||||||
or provisional_accounting_for_non_stock_items
|
or provisional_accounting_for_non_stock_items
|
||||||
|
or is_asset_pr
|
||||||
):
|
):
|
||||||
warehouse_account = get_warehouse_account_map(self.company)
|
warehouse_account = get_warehouse_account_map(self.company)
|
||||||
|
|
||||||
@@ -73,12 +76,6 @@ class StockController(AccountsController):
|
|||||||
gl_entries = self.get_gl_entries(warehouse_account)
|
gl_entries = self.get_gl_entries(warehouse_account)
|
||||||
make_gl_entries(gl_entries, from_repost=from_repost)
|
make_gl_entries(gl_entries, from_repost=from_repost)
|
||||||
|
|
||||||
elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1:
|
|
||||||
gl_entries = []
|
|
||||||
gl_entries = self.get_asset_gl_entry(gl_entries)
|
|
||||||
update_regional_gl_entries(gl_entries, self)
|
|
||||||
make_gl_entries(gl_entries, from_repost=from_repost)
|
|
||||||
|
|
||||||
def validate_serialized_batch(self):
|
def validate_serialized_batch(self):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
@@ -1225,8 +1222,3 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa
|
|||||||
repost_entries.append(repost_entry)
|
repost_entries.append(repost_entry)
|
||||||
|
|
||||||
return repost_entries
|
return repost_entries
|
||||||
|
|
||||||
|
|
||||||
@erpnext.allow_regional
|
|
||||||
def update_regional_gl_entries(gl_list, doc):
|
|
||||||
return
|
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -165,7 +166,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-07-30 16:39:09.775720",
|
"modified": "2023-11-14 18:35:30.887278",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Opportunity Item",
|
"name": "Opportunity Item",
|
||||||
@@ -173,5 +174,6 @@
|
|||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ frappe.ui.form.on("BOM Creator", {
|
|||||||
|| frappe.bom_configurator.bom_configurator !== frm.doc.name)) {
|
|| frappe.bom_configurator.bom_configurator !== frm.doc.name)) {
|
||||||
frm.trigger("build_tree");
|
frm.trigger("build_tree");
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!frm.doc.items?.length ) {
|
||||||
let $parent = $(frm.fields_dict["bom_creator"].wrapper);
|
let $parent = $(frm.fields_dict["bom_creator"].wrapper);
|
||||||
$parent.empty();
|
$parent.empty();
|
||||||
frm.trigger("make_new_entry");
|
frm.trigger("make_new_entry");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from collections import OrderedDict
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt
|
from frappe.utils import cint, flt
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_item_rate
|
from erpnext.manufacturing.doctype.bom.bom import get_bom_item_rate
|
||||||
|
|
||||||
@@ -91,11 +91,19 @@ class BOMCreator(Document):
|
|||||||
parent_reference = {row.idx: row.name for row in self.items}
|
parent_reference = {row.idx: row.name for row in self.items}
|
||||||
|
|
||||||
for row in self.items:
|
for row in self.items:
|
||||||
if row.fg_reference_id:
|
ref_id = ""
|
||||||
|
|
||||||
|
if row.parent_row_no:
|
||||||
|
ref_id = parent_reference.get(cint(row.parent_row_no))
|
||||||
|
|
||||||
|
# Check whether the reference id of the FG Item has correct or not
|
||||||
|
if row.fg_reference_id and row.fg_reference_id == ref_id:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if row.parent_row_no:
|
if row.parent_row_no:
|
||||||
row.fg_reference_id = parent_reference.get(row.parent_row_no)
|
row.fg_reference_id = ref_id
|
||||||
|
elif row.fg_item == self.item_code:
|
||||||
|
row.fg_reference_id = self.name
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_boms(self):
|
def add_boms(self):
|
||||||
|
|||||||
@@ -215,7 +215,6 @@
|
|||||||
"fieldname": "parent_row_no",
|
"fieldname": "parent_row_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Parent Row No",
|
"label": "Parent Row No",
|
||||||
"no_copy": 1,
|
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -231,7 +230,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-08-07 11:52:30.492233",
|
"modified": "2023-11-16 13:34:06.321061",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Creator Item",
|
"name": "BOM Creator Item",
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -169,7 +170,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-05-27 13:42:23.305455",
|
"modified": "2023-11-14 18:35:40.856895",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Explosion Item",
|
"name": "BOM Explosion Item",
|
||||||
|
|||||||
@@ -111,6 +111,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -289,7 +290,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-07-28 10:20:51.559010",
|
"modified": "2023-11-14 18:35:51.378513",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Item",
|
"name": "BOM Item",
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ function get_filters() {
|
|||||||
"label": __("Start Year"),
|
"label": __("Start Year"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
|
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
|
||||||
},
|
},
|
||||||
@@ -148,7 +147,6 @@ function get_filters() {
|
|||||||
"label": __("End Year"),
|
"label": __("End Year"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
|
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
|
||||||
},
|
},
|
||||||
@@ -197,5 +195,13 @@ function get_filters() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Dynamically set 'default' values for fiscal year filters
|
||||||
|
let fy_filters = filters.filter(x=>{return ["from_fiscal_year", "to_fiscal_year"].includes(x.fieldname);})
|
||||||
|
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), false, true);
|
||||||
|
if (fiscal_year) {
|
||||||
|
let fy = erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), false, false);
|
||||||
|
fy_filters.forEach(x=>{x.default = fy;})
|
||||||
|
}
|
||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ $.extend(erpnext.utils, {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
get_fiscal_year: function(date, with_dates=false) {
|
get_fiscal_year: function(date, with_dates=false, boolean=false) {
|
||||||
if(!date) {
|
if(!date) {
|
||||||
date = frappe.datetime.get_today();
|
date = frappe.datetime.get_today();
|
||||||
}
|
}
|
||||||
@@ -413,7 +413,8 @@ $.extend(erpnext.utils, {
|
|||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.utils.get_fiscal_year",
|
method: "erpnext.accounts.utils.get_fiscal_year",
|
||||||
args: {
|
args: {
|
||||||
date: date
|
date: date,
|
||||||
|
boolean: boolean
|
||||||
},
|
},
|
||||||
async: false,
|
async: false,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
|||||||
@@ -135,6 +135,7 @@
|
|||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -666,7 +667,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-09-26 13:42:11.410294",
|
"modified": "2023-11-14 18:24:24.619832",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation Item",
|
"name": "Quotation Item",
|
||||||
@@ -676,4 +677,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -767,8 +767,11 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None):
|
|||||||
if target.company_address:
|
if target.company_address:
|
||||||
target.update(get_fetch_values("Delivery Note", "company_address", target.company_address))
|
target.update(get_fetch_values("Delivery Note", "company_address", target.company_address))
|
||||||
|
|
||||||
# set target items names to ensure proper linking with packed_items
|
# if invoked in bulk creation, validations are ignored and thus this method is nerver invoked
|
||||||
target.set_new_name()
|
if frappe.flags.bulk_transaction:
|
||||||
|
# set target items names to ensure proper linking with packed_items
|
||||||
|
target.set_new_name()
|
||||||
|
|
||||||
make_packing_list(target)
|
make_packing_list(target)
|
||||||
|
|
||||||
def condition(doc):
|
def condition(doc):
|
||||||
|
|||||||
@@ -68,7 +68,6 @@
|
|||||||
"total_weight",
|
"total_weight",
|
||||||
"column_break_21",
|
"column_break_21",
|
||||||
"weight_uom",
|
"weight_uom",
|
||||||
"accounting_dimensions_section",
|
|
||||||
"warehouse_and_reference",
|
"warehouse_and_reference",
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"target_warehouse",
|
"target_warehouse",
|
||||||
@@ -177,6 +176,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -890,18 +890,12 @@
|
|||||||
"label": "Production Plan Qty",
|
"label": "Production Plan Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"collapsible": 1,
|
|
||||||
"fieldname": "accounting_dimensions_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Accounting Dimensions"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-17 18:18:26.475259",
|
"modified": "2023-11-14 18:37:12.787893",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order Item",
|
"name": "Sales Order Item",
|
||||||
|
|||||||
@@ -81,8 +81,10 @@ frappe.ui.form.on("Employee", {
|
|||||||
employee: frm.doc.name,
|
employee: frm.doc.name,
|
||||||
email: frm.doc.prefered_email
|
email: frm.doc.prefered_email
|
||||||
},
|
},
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __("Creating User..."),
|
||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
frm.set_value("user_id", r.message);
|
frm.reload_doc();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ class Employee(NestedSet):
|
|||||||
else:
|
else:
|
||||||
existing_user_id = frappe.db.get_value("Employee", self.name, "user_id")
|
existing_user_id = frappe.db.get_value("Employee", self.name, "user_id")
|
||||||
if existing_user_id:
|
if existing_user_id:
|
||||||
|
user = frappe.get_doc("User", existing_user_id)
|
||||||
|
validate_employee_role(user, ignore_emp_check=True)
|
||||||
|
user.save(ignore_permissions=True)
|
||||||
remove_user_permission("Employee", self.name, existing_user_id)
|
remove_user_permission("Employee", self.name, existing_user_id)
|
||||||
|
|
||||||
def after_rename(self, old, new, merge):
|
def after_rename(self, old, new, merge):
|
||||||
@@ -230,12 +233,26 @@ class Employee(NestedSet):
|
|||||||
frappe.cache().hdel("employees_with_number", prev_number)
|
frappe.cache().hdel("employees_with_number", prev_number)
|
||||||
|
|
||||||
|
|
||||||
def validate_employee_role(doc, method):
|
def validate_employee_role(doc, method=None, ignore_emp_check=False):
|
||||||
# called via User hook
|
# called via User hook
|
||||||
if "Employee" in [d.role for d in doc.get("roles")]:
|
if not ignore_emp_check:
|
||||||
if not frappe.db.get_value("Employee", {"user_id": doc.name}):
|
if frappe.db.get_value("Employee", {"user_id": doc.name}):
|
||||||
frappe.msgprint(_("Please set User ID field in an Employee record to set Employee Role"))
|
return
|
||||||
doc.get("roles").remove(doc.get("roles", {"role": "Employee"})[0])
|
|
||||||
|
user_roles = [d.role for d in doc.get("roles")]
|
||||||
|
if "Employee" in user_roles:
|
||||||
|
frappe.msgprint(
|
||||||
|
_("User {0}: Removed Employee role as there is no mapped employee.").format(doc.name)
|
||||||
|
)
|
||||||
|
doc.get("roles").remove(doc.get("roles", {"role": "Employee"})[0])
|
||||||
|
|
||||||
|
if "Employee Self Service" in user_roles:
|
||||||
|
frappe.msgprint(
|
||||||
|
_("User {0}: Removed Employee Self Service role as there is no mapped employee.").format(
|
||||||
|
doc.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
doc.get("roles").remove(doc.get("roles", {"role": "Employee Self Service"})[0])
|
||||||
|
|
||||||
|
|
||||||
def update_user_permissions(doc, method):
|
def update_user_permissions(doc, method):
|
||||||
@@ -347,6 +364,8 @@ def create_user(employee, user=None, email=None):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
user.insert()
|
user.insert()
|
||||||
|
emp.user_id = user.name
|
||||||
|
emp.save()
|
||||||
return user.name
|
return user.name
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,15 @@ class TestEmployee(unittest.TestCase):
|
|||||||
employee1_doc.status = "Left"
|
employee1_doc.status = "Left"
|
||||||
self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
|
self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
|
||||||
|
|
||||||
|
def test_user_has_employee(self):
|
||||||
|
employee = make_employee("test_emp_user_creation@company.com")
|
||||||
|
employee_doc = frappe.get_doc("Employee", employee)
|
||||||
|
user = employee_doc.user_id
|
||||||
|
self.assertTrue("Employee" in frappe.get_roles(user))
|
||||||
|
employee_doc.user_id = ""
|
||||||
|
employee_doc.save()
|
||||||
|
self.assertTrue("Employee" not in frappe.get_roles(user))
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,7 @@
|
|||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -893,7 +894,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-16 16:18:18.013379",
|
"modified": "2023-11-14 18:37:38.638144",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note Item",
|
"name": "Delivery Note Item",
|
||||||
|
|||||||
@@ -110,6 +110,7 @@
|
|||||||
"width": "250px"
|
"width": "250px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach Image",
|
"fieldtype": "Attach Image",
|
||||||
"label": "Image",
|
"label": "Image",
|
||||||
@@ -478,7 +479,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-27 15:53:41.444236",
|
"modified": "2023-11-14 18:37:59.599115",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Material Request Item",
|
"name": "Material Request Item",
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from pypika import functions as fn
|
|||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
|
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
|
||||||
from erpnext.buying.utils import check_on_hold_or_closed_status
|
from erpnext.buying.utils import check_on_hold_or_closed_status
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction
|
||||||
@@ -144,8 +143,8 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
|
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
|
||||||
# check cwip accounts before making auto assets
|
# check cwip accounts before making auto assets
|
||||||
# Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account
|
# Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account
|
||||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
self.get_company_default("asset_received_but_not_billed")
|
||||||
cwip_account = get_asset_account(
|
get_asset_account(
|
||||||
"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
|
"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@@ -314,7 +313,6 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account)
|
self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account)
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
self.get_asset_gl_entry(gl_entries)
|
|
||||||
update_regional_gl_entries(gl_entries, self)
|
update_regional_gl_entries(gl_entries, self)
|
||||||
|
|
||||||
return process_gl_map(gl_entries)
|
return process_gl_map(gl_entries)
|
||||||
@@ -324,14 +322,6 @@ class PurchaseReceipt(BuyingController):
|
|||||||
get_purchase_document_details,
|
get_purchase_document_details,
|
||||||
)
|
)
|
||||||
|
|
||||||
stock_rbnb = None
|
|
||||||
if erpnext.is_perpetual_inventory_enabled(self.company):
|
|
||||||
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
|
|
||||||
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
|
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
|
||||||
|
|
||||||
warehouse_with_no_account = []
|
|
||||||
stock_items = self.get_stock_items()
|
|
||||||
provisional_accounting_for_non_stock_items = cint(
|
provisional_accounting_for_non_stock_items = cint(
|
||||||
frappe.db.get_value(
|
frappe.db.get_value(
|
||||||
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
|
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
|
||||||
@@ -340,28 +330,258 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
exchange_rate_map, net_rate_map = get_purchase_document_details(self)
|
exchange_rate_map, net_rate_map = get_purchase_document_details(self)
|
||||||
|
|
||||||
|
def validate_account(account_type):
|
||||||
|
frappe.throw(_("{0} account not found while submitting purchase receipt").format(account_type))
|
||||||
|
|
||||||
|
def make_item_asset_inward_gl_entry(item, stock_value_diff, stock_asset_account_name):
|
||||||
|
account_currency = get_account_currency(stock_asset_account_name)
|
||||||
|
|
||||||
|
if not stock_asset_account_name:
|
||||||
|
validate_account("Asset or warehouse account")
|
||||||
|
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=stock_asset_account_name,
|
||||||
|
cost_center=d.cost_center,
|
||||||
|
debit=stock_value_diff,
|
||||||
|
credit=0.0,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=stock_asset_rbnb,
|
||||||
|
account_currency=account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_stock_received_but_not_billed_entry(item):
|
||||||
|
account = (
|
||||||
|
warehouse_account[item.from_warehouse]["account"] if item.from_warehouse else stock_asset_rbnb
|
||||||
|
)
|
||||||
|
account_currency = get_account_currency(account)
|
||||||
|
|
||||||
|
# GL Entry for from warehouse or Stock Received but not billed
|
||||||
|
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
|
||||||
|
credit_amount = (
|
||||||
|
flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||||
|
if account_currency == self.company_currency
|
||||||
|
else flt(item.net_amount, item.precision("net_amount"))
|
||||||
|
)
|
||||||
|
|
||||||
|
outgoing_amount = item.base_net_amount
|
||||||
|
if self.is_internal_transfer() and item.valuation_rate:
|
||||||
|
outgoing_amount = abs(get_stock_value_difference(self.name, item.name, item.from_warehouse))
|
||||||
|
credit_amount = outgoing_amount
|
||||||
|
|
||||||
|
if credit_amount:
|
||||||
|
if not account:
|
||||||
|
validate_account("Stock or Asset Received But Not Billed")
|
||||||
|
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=account,
|
||||||
|
cost_center=item.cost_center,
|
||||||
|
debit=-1 * flt(outgoing_amount, item.precision("base_net_amount")),
|
||||||
|
credit=0.0,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=stock_asset_account_name,
|
||||||
|
debit_in_account_currency=-1 * flt(outgoing_amount, item.precision("base_net_amount")),
|
||||||
|
account_currency=account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
# check if the exchange rate has changed
|
||||||
|
if d.get("purchase_invoice"):
|
||||||
|
if (
|
||||||
|
exchange_rate_map[item.purchase_invoice]
|
||||||
|
and self.conversion_rate != exchange_rate_map[item.purchase_invoice]
|
||||||
|
and item.net_rate == net_rate_map[item.purchase_invoice_item]
|
||||||
|
):
|
||||||
|
|
||||||
|
discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * (
|
||||||
|
exchange_rate_map[item.purchase_invoice] - self.conversion_rate
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=account,
|
||||||
|
cost_center=item.cost_center,
|
||||||
|
debit=0.0,
|
||||||
|
credit=discrepancy_caused_by_exchange_rate_difference,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=self.supplier,
|
||||||
|
debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
|
||||||
|
account_currency=account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=self.get_company_default("exchange_gain_loss_account"),
|
||||||
|
cost_center=d.cost_center,
|
||||||
|
debit=discrepancy_caused_by_exchange_rate_difference,
|
||||||
|
credit=0.0,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=self.supplier,
|
||||||
|
debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
|
||||||
|
account_currency=account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
return outgoing_amount
|
||||||
|
|
||||||
|
def make_landed_cost_gl_entries(item):
|
||||||
|
# Amount added through landed-cost-voucher
|
||||||
|
if item.landed_cost_voucher_amount and landed_cost_entries:
|
||||||
|
if (item.item_code, item.name) in landed_cost_entries:
|
||||||
|
for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
|
||||||
|
account_currency = get_account_currency(account)
|
||||||
|
credit_amount = (
|
||||||
|
flt(amount["base_amount"])
|
||||||
|
if (amount["base_amount"] or account_currency != self.company_currency)
|
||||||
|
else flt(amount["amount"])
|
||||||
|
)
|
||||||
|
|
||||||
|
if not account:
|
||||||
|
validate_account("Landed Cost Account")
|
||||||
|
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=account,
|
||||||
|
cost_center=item.cost_center,
|
||||||
|
debit=0.0,
|
||||||
|
credit=credit_amount,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=stock_asset_account_name,
|
||||||
|
credit_in_account_currency=flt(amount["amount"]),
|
||||||
|
account_currency=account_currency,
|
||||||
|
project=item.project,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_rate_difference_entry(item):
|
||||||
|
if item.rate_difference_with_purchase_invoice and stock_asset_rbnb:
|
||||||
|
account_currency = get_account_currency(stock_asset_rbnb)
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=stock_asset_rbnb,
|
||||||
|
cost_center=item.cost_center,
|
||||||
|
debit=0.0,
|
||||||
|
credit=flt(item.rate_difference_with_purchase_invoice),
|
||||||
|
remarks=_("Adjustment based on Purchase Invoice rate"),
|
||||||
|
against_account=stock_asset_account_name,
|
||||||
|
account_currency=account_currency,
|
||||||
|
project=item.project,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_sub_contracting_gl_entries(item):
|
||||||
|
# sub-contracting warehouse
|
||||||
|
if flt(item.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=supplier_warehouse_account,
|
||||||
|
cost_center=item.cost_center,
|
||||||
|
debit=0.0,
|
||||||
|
credit=flt(item.rm_supp_cost),
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=stock_asset_account_name,
|
||||||
|
account_currency=supplier_warehouse_account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_divisional_loss_gl_entry(item, outgoing_amount):
|
||||||
|
if item.is_fixed_asset:
|
||||||
|
return
|
||||||
|
|
||||||
|
# divisional loss adjustment
|
||||||
|
valuation_amount_as_per_doc = (
|
||||||
|
flt(outgoing_amount, d.precision("base_net_amount"))
|
||||||
|
+ flt(item.landed_cost_voucher_amount)
|
||||||
|
+ flt(item.rm_supp_cost)
|
||||||
|
+ flt(item.item_tax_amount)
|
||||||
|
+ flt(item.rate_difference_with_purchase_invoice)
|
||||||
|
)
|
||||||
|
|
||||||
|
divisional_loss = flt(
|
||||||
|
valuation_amount_as_per_doc - flt(stock_value_diff), item.precision("base_net_amount")
|
||||||
|
)
|
||||||
|
|
||||||
|
if divisional_loss:
|
||||||
|
loss_account = (
|
||||||
|
self.get_company_default("default_expense_account", ignore_validation=True)
|
||||||
|
or stock_asset_rbnb
|
||||||
|
)
|
||||||
|
|
||||||
|
cost_center = item.cost_center or frappe.get_cached_value(
|
||||||
|
"Company", self.company, "cost_center"
|
||||||
|
)
|
||||||
|
account_currency = get_account_currency(loss_account)
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=loss_account,
|
||||||
|
cost_center=cost_center,
|
||||||
|
debit=divisional_loss,
|
||||||
|
credit=0.0,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=stock_asset_account_name,
|
||||||
|
account_currency=account_currency,
|
||||||
|
project=item.project,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
|
stock_items = self.get_stock_items()
|
||||||
|
warehouse_with_no_account = []
|
||||||
|
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.item_code in stock_items and flt(d.qty) and (flt(d.valuation_rate) or self.is_return):
|
if (
|
||||||
if warehouse_account.get(d.warehouse):
|
provisional_accounting_for_non_stock_items
|
||||||
stock_value_diff = frappe.db.get_value(
|
and d.item_code not in stock_items
|
||||||
"Stock Ledger Entry",
|
and flt(d.qty)
|
||||||
{
|
and d.get("provisional_expense_account")
|
||||||
"voucher_type": "Purchase Receipt",
|
and not d.is_fixed_asset
|
||||||
"voucher_no": self.name,
|
):
|
||||||
"voucher_detail_no": d.name,
|
self.add_provisional_gl_entry(
|
||||||
"warehouse": d.warehouse,
|
d, gl_entries, self.posting_date, d.get("provisional_expense_account")
|
||||||
"is_cancelled": 0,
|
)
|
||||||
},
|
elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return):
|
||||||
"stock_value_difference",
|
remarks = self.get("remarks") or _("Accounting Entry for {0}").format(
|
||||||
|
"Asset" if d.is_fixed_asset else "Stock"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not (
|
||||||
|
(erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items)
|
||||||
|
or d.is_fixed_asset
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
stock_asset_rbnb = (
|
||||||
|
self.get_company_default("asset_received_but_not_billed")
|
||||||
|
if d.is_fixed_asset
|
||||||
|
else self.get_company_default("stock_received_but_not_billed")
|
||||||
|
)
|
||||||
|
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
|
||||||
|
|
||||||
|
if d.is_fixed_asset:
|
||||||
|
account_type = (
|
||||||
|
"capital_work_in_progress_account"
|
||||||
|
if is_cwip_accounting_enabled(d.asset_category)
|
||||||
|
else "fixed_asset_account"
|
||||||
)
|
)
|
||||||
|
|
||||||
warehouse_account_name = warehouse_account[d.warehouse]["account"]
|
stock_asset_account_name = get_asset_account(
|
||||||
warehouse_account_currency = warehouse_account[d.warehouse]["account_currency"]
|
account_type, asset_category=d.asset_category, company=self.company
|
||||||
|
)
|
||||||
|
|
||||||
|
stock_value_diff = (
|
||||||
|
flt(d.net_amount)
|
||||||
|
+ flt(d.item_tax_amount / self.conversion_rate)
|
||||||
|
+ flt(d.landed_cost_voucher_amount)
|
||||||
|
)
|
||||||
|
elif warehouse_account.get(d.warehouse):
|
||||||
|
stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse)
|
||||||
|
stock_asset_account_name = warehouse_account[d.warehouse]["account"]
|
||||||
supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account")
|
supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account")
|
||||||
supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get(
|
supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get(
|
||||||
"account_currency"
|
"account_currency"
|
||||||
)
|
)
|
||||||
remarks = self.get("remarks") or _("Accounting Entry for Stock")
|
|
||||||
|
|
||||||
# If PR is sub-contracted and fg item rate is zero
|
# If PR is sub-contracted and fg item rate is zero
|
||||||
# in that case if account for source and target warehouse are same,
|
# in that case if account for source and target warehouse are same,
|
||||||
@@ -369,213 +589,24 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if (
|
if (
|
||||||
flt(stock_value_diff) == flt(d.rm_supp_cost)
|
flt(stock_value_diff) == flt(d.rm_supp_cost)
|
||||||
and warehouse_account.get(self.supplier_warehouse)
|
and warehouse_account.get(self.supplier_warehouse)
|
||||||
and warehouse_account_name == supplier_warehouse_account
|
and stock_asset_account_name == supplier_warehouse_account
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.add_gl_entry(
|
if (flt(d.valuation_rate) or self.is_return or d.is_fixed_asset) and flt(d.qty):
|
||||||
gl_entries=gl_entries,
|
make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name)
|
||||||
account=warehouse_account_name,
|
outgoing_amount = make_stock_received_but_not_billed_entry(d)
|
||||||
cost_center=d.cost_center,
|
make_landed_cost_gl_entries(d)
|
||||||
debit=stock_value_diff,
|
make_rate_difference_entry(d)
|
||||||
credit=0.0,
|
make_sub_contracting_gl_entries(d)
|
||||||
remarks=remarks,
|
make_divisional_loss_gl_entry(d, outgoing_amount)
|
||||||
against_account=stock_rbnb,
|
elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or (
|
||||||
account_currency=warehouse_account_currency,
|
d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
# GL Entry for from warehouse or Stock Received but not billed
|
|
||||||
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
|
|
||||||
credit_currency = (
|
|
||||||
get_account_currency(warehouse_account[d.from_warehouse]["account"])
|
|
||||||
if d.from_warehouse
|
|
||||||
else get_account_currency(stock_rbnb)
|
|
||||||
)
|
|
||||||
|
|
||||||
credit_amount = (
|
|
||||||
flt(d.base_net_amount, d.precision("base_net_amount"))
|
|
||||||
if credit_currency == self.company_currency
|
|
||||||
else flt(d.net_amount, d.precision("net_amount"))
|
|
||||||
)
|
|
||||||
|
|
||||||
outgoing_amount = d.base_net_amount
|
|
||||||
if self.is_internal_transfer() and d.valuation_rate:
|
|
||||||
outgoing_amount = abs(
|
|
||||||
frappe.db.get_value(
|
|
||||||
"Stock Ledger Entry",
|
|
||||||
{
|
|
||||||
"voucher_type": "Purchase Receipt",
|
|
||||||
"voucher_no": self.name,
|
|
||||||
"voucher_detail_no": d.name,
|
|
||||||
"warehouse": d.from_warehouse,
|
|
||||||
"is_cancelled": 0,
|
|
||||||
},
|
|
||||||
"stock_value_difference",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
credit_amount = outgoing_amount
|
|
||||||
|
|
||||||
if credit_amount:
|
|
||||||
account = warehouse_account[d.from_warehouse]["account"] if d.from_warehouse else stock_rbnb
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=account,
|
|
||||||
cost_center=d.cost_center,
|
|
||||||
debit=-1 * flt(outgoing_amount, d.precision("base_net_amount")),
|
|
||||||
credit=0.0,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=warehouse_account_name,
|
|
||||||
debit_in_account_currency=-1 * credit_amount,
|
|
||||||
account_currency=credit_currency,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
# check if the exchange rate has changed
|
|
||||||
if d.get("purchase_invoice"):
|
|
||||||
if (
|
|
||||||
exchange_rate_map[d.purchase_invoice]
|
|
||||||
and self.conversion_rate != exchange_rate_map[d.purchase_invoice]
|
|
||||||
and d.net_rate == net_rate_map[d.purchase_invoice_item]
|
|
||||||
):
|
|
||||||
|
|
||||||
discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * (
|
|
||||||
exchange_rate_map[d.purchase_invoice] - self.conversion_rate
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=account,
|
|
||||||
cost_center=d.cost_center,
|
|
||||||
debit=0.0,
|
|
||||||
credit=discrepancy_caused_by_exchange_rate_difference,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=self.supplier,
|
|
||||||
debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
|
|
||||||
account_currency=credit_currency,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=self.get_company_default("exchange_gain_loss_account"),
|
|
||||||
cost_center=d.cost_center,
|
|
||||||
debit=discrepancy_caused_by_exchange_rate_difference,
|
|
||||||
credit=0.0,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=self.supplier,
|
|
||||||
debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
|
|
||||||
account_currency=credit_currency,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Amount added through landed-cos-voucher
|
|
||||||
if d.landed_cost_voucher_amount and landed_cost_entries:
|
|
||||||
if (d.item_code, d.name) in landed_cost_entries:
|
|
||||||
for account, amount in landed_cost_entries[(d.item_code, d.name)].items():
|
|
||||||
account_currency = get_account_currency(account)
|
|
||||||
credit_amount = (
|
|
||||||
flt(amount["base_amount"])
|
|
||||||
if (amount["base_amount"] or account_currency != self.company_currency)
|
|
||||||
else flt(amount["amount"])
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=account,
|
|
||||||
cost_center=d.cost_center,
|
|
||||||
debit=0.0,
|
|
||||||
credit=credit_amount,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=warehouse_account_name,
|
|
||||||
credit_in_account_currency=flt(amount["amount"]),
|
|
||||||
account_currency=account_currency,
|
|
||||||
project=d.project,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
if d.rate_difference_with_purchase_invoice and stock_rbnb:
|
|
||||||
account_currency = get_account_currency(stock_rbnb)
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=stock_rbnb,
|
|
||||||
cost_center=d.cost_center,
|
|
||||||
debit=0.0,
|
|
||||||
credit=flt(d.rate_difference_with_purchase_invoice),
|
|
||||||
remarks=_("Adjustment based on Purchase Invoice rate"),
|
|
||||||
against_account=warehouse_account_name,
|
|
||||||
account_currency=account_currency,
|
|
||||||
project=d.project,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
# sub-contracting warehouse
|
|
||||||
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=supplier_warehouse_account,
|
|
||||||
cost_center=d.cost_center,
|
|
||||||
debit=0.0,
|
|
||||||
credit=flt(d.rm_supp_cost),
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=warehouse_account_name,
|
|
||||||
account_currency=supplier_warehouse_account_currency,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
# divisional loss adjustment
|
|
||||||
valuation_amount_as_per_doc = (
|
|
||||||
flt(outgoing_amount, d.precision("base_net_amount"))
|
|
||||||
+ flt(d.landed_cost_voucher_amount)
|
|
||||||
+ flt(d.rm_supp_cost)
|
|
||||||
+ flt(d.item_tax_amount)
|
|
||||||
+ flt(d.rate_difference_with_purchase_invoice)
|
|
||||||
)
|
|
||||||
|
|
||||||
divisional_loss = flt(
|
|
||||||
valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount")
|
|
||||||
)
|
|
||||||
|
|
||||||
if divisional_loss:
|
|
||||||
if self.is_return or flt(d.item_tax_amount):
|
|
||||||
loss_account = expenses_included_in_valuation
|
|
||||||
else:
|
|
||||||
loss_account = (
|
|
||||||
self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb
|
|
||||||
)
|
|
||||||
|
|
||||||
cost_center = d.cost_center or frappe.get_cached_value(
|
|
||||||
"Company", self.company, "cost_center"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=loss_account,
|
|
||||||
cost_center=cost_center,
|
|
||||||
debit=divisional_loss,
|
|
||||||
credit=0.0,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=warehouse_account_name,
|
|
||||||
account_currency=credit_currency,
|
|
||||||
project=d.project,
|
|
||||||
item=d,
|
|
||||||
)
|
|
||||||
|
|
||||||
elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or (
|
|
||||||
d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account
|
|
||||||
):
|
|
||||||
warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse)
|
|
||||||
elif (
|
|
||||||
d.item_code not in stock_items
|
|
||||||
and not d.is_fixed_asset
|
|
||||||
and flt(d.qty)
|
|
||||||
and provisional_accounting_for_non_stock_items
|
|
||||||
and d.get("provisional_expense_account")
|
|
||||||
):
|
):
|
||||||
self.add_provisional_gl_entry(
|
warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse)
|
||||||
d, gl_entries, self.posting_date, d.get("provisional_expense_account")
|
|
||||||
)
|
if d.is_fixed_asset:
|
||||||
|
self.update_assets(d, d.valuation_rate)
|
||||||
|
|
||||||
if warehouse_with_no_account:
|
if warehouse_with_no_account:
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
@@ -588,8 +619,8 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self, item, gl_entries, posting_date, provisional_account, reverse=0
|
self, item, gl_entries, posting_date, provisional_account, reverse=0
|
||||||
):
|
):
|
||||||
credit_currency = get_account_currency(provisional_account)
|
credit_currency = get_account_currency(provisional_account)
|
||||||
debit_currency = get_account_currency(item.expense_account)
|
|
||||||
expense_account = item.expense_account
|
expense_account = item.expense_account
|
||||||
|
debit_currency = get_account_currency(item.expense_account)
|
||||||
remarks = self.get("remarks") or _("Accounting Entry for Service")
|
remarks = self.get("remarks") or _("Accounting Entry for Service")
|
||||||
multiplication_factor = 1
|
multiplication_factor = 1
|
||||||
|
|
||||||
@@ -630,11 +661,8 @@ class PurchaseReceipt(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_tax_gl_entries(self, gl_entries):
|
def make_tax_gl_entries(self, gl_entries):
|
||||||
|
|
||||||
if erpnext.is_perpetual_inventory_enabled(self.company):
|
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
|
||||||
|
|
||||||
negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")])
|
negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")])
|
||||||
|
is_asset_pr = any(d.is_fixed_asset for d in self.get("items"))
|
||||||
# Cost center-wise amount breakup for other charges included for valuation
|
# Cost center-wise amount breakup for other charges included for valuation
|
||||||
valuation_tax = {}
|
valuation_tax = {}
|
||||||
for tax in self.get("taxes"):
|
for tax in self.get("taxes"):
|
||||||
@@ -654,26 +682,26 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
if negative_expense_to_be_booked and valuation_tax:
|
if negative_expense_to_be_booked and valuation_tax:
|
||||||
# Backward compatibility:
|
# Backward compatibility:
|
||||||
# If expenses_included_in_valuation account has been credited in against PI
|
|
||||||
# and charges added via Landed Cost Voucher,
|
# and charges added via Landed Cost Voucher,
|
||||||
# post valuation related charges on "Stock Received But Not Billed"
|
# post valuation related charges on "Stock Received But Not Billed"
|
||||||
# introduced in 2014 for backward compatibility of expenses already booked in expenses_included_in_valuation account
|
|
||||||
|
|
||||||
negative_expense_booked_in_pi = frappe.db.sql(
|
|
||||||
"""select name from `tabPurchase Invoice Item` pi
|
|
||||||
where docstatus = 1 and purchase_receipt=%s
|
|
||||||
and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
|
|
||||||
and voucher_no=pi.parent and account=%s)""",
|
|
||||||
(self.name, expenses_included_in_valuation),
|
|
||||||
)
|
|
||||||
|
|
||||||
against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
|
against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
|
||||||
total_valuation_amount = sum(valuation_tax.values())
|
total_valuation_amount = sum(valuation_tax.values())
|
||||||
amount_including_divisional_loss = negative_expense_to_be_booked
|
amount_including_divisional_loss = negative_expense_to_be_booked
|
||||||
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
|
stock_rbnb = (
|
||||||
|
self.get("asset_received_but_not_billed")
|
||||||
|
if is_asset_pr
|
||||||
|
else self.get_company_default("stock_received_but_not_billed")
|
||||||
|
)
|
||||||
i = 1
|
i = 1
|
||||||
for tax in self.get("taxes"):
|
for tax in self.get("taxes"):
|
||||||
if valuation_tax.get(tax.name):
|
if valuation_tax.get(tax.name):
|
||||||
|
negative_expense_booked_in_pi = frappe.db.sql(
|
||||||
|
"""select name from `tabPurchase Invoice Item` pi
|
||||||
|
where docstatus = 1 and purchase_receipt=%s
|
||||||
|
and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
|
||||||
|
and voucher_no=pi.parent and account=%s)""",
|
||||||
|
(self.name, tax.account_head),
|
||||||
|
)
|
||||||
|
|
||||||
if negative_expense_booked_in_pi:
|
if negative_expense_booked_in_pi:
|
||||||
account = stock_rbnb
|
account = stock_rbnb
|
||||||
@@ -701,103 +729,6 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def get_asset_gl_entry(self, gl_entries):
|
|
||||||
for item in self.get("items"):
|
|
||||||
if item.is_fixed_asset:
|
|
||||||
if is_cwip_accounting_enabled(item.asset_category):
|
|
||||||
self.add_asset_gl_entries(item, gl_entries)
|
|
||||||
if flt(item.landed_cost_voucher_amount):
|
|
||||||
self.add_lcv_gl_entries(item, gl_entries)
|
|
||||||
# update assets gross amount by its valuation rate
|
|
||||||
# valuation rate is total of net rate, raw mat supp cost, tax amount, lcv amount per item
|
|
||||||
self.update_assets(item, item.valuation_rate)
|
|
||||||
return gl_entries
|
|
||||||
|
|
||||||
def add_asset_gl_entries(self, item, gl_entries):
|
|
||||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
|
||||||
# This returns category's cwip account if not then fallback to company's default cwip account
|
|
||||||
cwip_account = get_asset_account(
|
|
||||||
"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
|
|
||||||
)
|
|
||||||
|
|
||||||
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate)
|
|
||||||
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
|
||||||
remarks = self.get("remarks") or _("Accounting Entry for Asset")
|
|
||||||
|
|
||||||
cwip_account_currency = get_account_currency(cwip_account)
|
|
||||||
# debit cwip account
|
|
||||||
debit_in_account_currency = (
|
|
||||||
base_asset_amount if cwip_account_currency == self.company_currency else asset_amount
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=cwip_account,
|
|
||||||
cost_center=item.cost_center,
|
|
||||||
debit=base_asset_amount,
|
|
||||||
credit=0.0,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=arbnb_account,
|
|
||||||
debit_in_account_currency=debit_in_account_currency,
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
|
|
||||||
asset_rbnb_currency = get_account_currency(arbnb_account)
|
|
||||||
# credit arbnb account
|
|
||||||
credit_in_account_currency = (
|
|
||||||
base_asset_amount if asset_rbnb_currency == self.company_currency else asset_amount
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=arbnb_account,
|
|
||||||
cost_center=item.cost_center,
|
|
||||||
debit=0.0,
|
|
||||||
credit=base_asset_amount,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=cwip_account,
|
|
||||||
credit_in_account_currency=credit_in_account_currency,
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_lcv_gl_entries(self, item, gl_entries):
|
|
||||||
expenses_included_in_asset_valuation = self.get_company_default(
|
|
||||||
"expenses_included_in_asset_valuation"
|
|
||||||
)
|
|
||||||
if not is_cwip_accounting_enabled(item.asset_category):
|
|
||||||
asset_account = get_asset_category_account(
|
|
||||||
asset_category=item.asset_category, fieldname="fixed_asset_account", company=self.company
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# This returns company's default cwip account
|
|
||||||
asset_account = get_asset_account("capital_work_in_progress_account", company=self.company)
|
|
||||||
|
|
||||||
remarks = self.get("remarks") or _("Accounting Entry for Stock")
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=expenses_included_in_asset_valuation,
|
|
||||||
cost_center=item.cost_center,
|
|
||||||
debit=0.0,
|
|
||||||
credit=flt(item.landed_cost_voucher_amount),
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=asset_account,
|
|
||||||
project=item.project,
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_gl_entry(
|
|
||||||
gl_entries=gl_entries,
|
|
||||||
account=asset_account,
|
|
||||||
cost_center=item.cost_center,
|
|
||||||
debit=flt(item.landed_cost_voucher_amount),
|
|
||||||
credit=0.0,
|
|
||||||
remarks=remarks,
|
|
||||||
against_account=expenses_included_in_asset_valuation,
|
|
||||||
project=item.project,
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_assets(self, item, valuation_rate):
|
def update_assets(self, item, valuation_rate):
|
||||||
assets = frappe.db.get_all(
|
assets = frappe.db.get_all(
|
||||||
"Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code}
|
"Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code}
|
||||||
@@ -869,6 +800,20 @@ class PurchaseReceipt(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse):
|
||||||
|
return frappe.db.get_value(
|
||||||
|
"Stock Ledger Entry",
|
||||||
|
{
|
||||||
|
"voucher_type": "Purchase Receipt",
|
||||||
|
"voucher_no": voucher_no,
|
||||||
|
"voucher_detail_no": voucher_detail_no,
|
||||||
|
"warehouse": warehouse,
|
||||||
|
"is_cancelled": 0,
|
||||||
|
},
|
||||||
|
"stock_value_difference",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None):
|
def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None):
|
||||||
po_billed_amt_details = get_billed_amount_against_po(po_details)
|
po_billed_amt_details = get_billed_amount_against_po(po_details)
|
||||||
|
|
||||||
|
|||||||
@@ -192,6 +192,7 @@
|
|||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -1090,7 +1091,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-30 17:32:24.560337",
|
"modified": "2023-11-14 18:38:15.251994",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -337,7 +338,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-01-20 23:25:45.363281",
|
"modified": "2023-11-14 18:38:37.640677",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Order Item",
|
"name": "Subcontracting Order Item",
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.image",
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@@ -521,7 +522,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-09-03 17:04:21.214316",
|
"modified": "2023-11-14 18:38:26.459669",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Receipt Item",
|
"name": "Subcontracting Receipt Item",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from datetime import date, datetime
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.utils import get_link_to_form, today
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -28,6 +29,48 @@ def transaction_processing(data, from_doctype, to_doctype):
|
|||||||
job(deserialized_data, from_doctype, to_doctype)
|
job(deserialized_data, from_doctype, to_doctype)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def retry(date: str | None):
|
||||||
|
if date:
|
||||||
|
failed_docs = frappe.db.get_all(
|
||||||
|
"Bulk Transaction Log Detail",
|
||||||
|
filters={"date": date, "transaction_status": "Failed", "retried": 0},
|
||||||
|
fields=["name", "transaction_name", "from_doctype", "to_doctype"],
|
||||||
|
)
|
||||||
|
if not failed_docs:
|
||||||
|
frappe.msgprint(_("There are no Failed transactions"))
|
||||||
|
else:
|
||||||
|
job = frappe.enqueue(
|
||||||
|
retry_failed_transactions,
|
||||||
|
failed_docs=failed_docs,
|
||||||
|
)
|
||||||
|
frappe.msgprint(
|
||||||
|
_("Job: {0} has been triggered for processing failed transactions").format(
|
||||||
|
get_link_to_form("RQ Job", job.id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def retry_failed_transactions(failed_docs: list | None):
|
||||||
|
if failed_docs:
|
||||||
|
for log in failed_docs:
|
||||||
|
try:
|
||||||
|
frappe.db.savepoint("before_creation_state")
|
||||||
|
task(log.transaction_name, log.from_doctype, log.to_doctype)
|
||||||
|
except Exception as e:
|
||||||
|
frappe.db.rollback(save_point="before_creation_state")
|
||||||
|
update_log(log.name, "Failed", 1, str(frappe.get_traceback()))
|
||||||
|
else:
|
||||||
|
update_log(log.name, "Success", 1)
|
||||||
|
|
||||||
|
|
||||||
|
def update_log(log_name, status, retried, err=None):
|
||||||
|
frappe.db.set_value("Bulk Transaction Log Detail", log_name, "transaction_status", status)
|
||||||
|
frappe.db.set_value("Bulk Transaction Log Detail", log_name, "retried", retried)
|
||||||
|
if err:
|
||||||
|
frappe.db.set_value("Bulk Transaction Log Detail", log_name, "error_description", err)
|
||||||
|
|
||||||
|
|
||||||
def job(deserialized_data, from_doctype, to_doctype):
|
def job(deserialized_data, from_doctype, to_doctype):
|
||||||
fail_count = 0
|
fail_count = 0
|
||||||
for d in deserialized_data:
|
for d in deserialized_data:
|
||||||
@@ -38,7 +81,7 @@ def job(deserialized_data, from_doctype, to_doctype):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
frappe.db.rollback(save_point="before_creation_state")
|
frappe.db.rollback(save_point="before_creation_state")
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
update_logger(
|
create_log(
|
||||||
doc_name,
|
doc_name,
|
||||||
str(frappe.get_traceback()),
|
str(frappe.get_traceback()),
|
||||||
from_doctype,
|
from_doctype,
|
||||||
@@ -47,7 +90,7 @@ def job(deserialized_data, from_doctype, to_doctype):
|
|||||||
log_date=str(date.today()),
|
log_date=str(date.today()),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
update_logger(
|
create_log(
|
||||||
doc_name, None, from_doctype, to_doctype, status="Success", log_date=str(date.today())
|
doc_name, None, from_doctype, to_doctype, status="Success", log_date=str(date.today())
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -98,6 +141,7 @@ def task(doc_name, from_doctype, to_doctype):
|
|||||||
},
|
},
|
||||||
"Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice},
|
"Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice},
|
||||||
}
|
}
|
||||||
|
frappe.flags.bulk_transaction = True
|
||||||
if to_doctype in ["Payment Entry"]:
|
if to_doctype in ["Payment Entry"]:
|
||||||
obj = mapper[from_doctype][to_doctype](from_doctype, doc_name)
|
obj = mapper[from_doctype][to_doctype](from_doctype, doc_name)
|
||||||
else:
|
else:
|
||||||
@@ -106,47 +150,21 @@ def task(doc_name, from_doctype, to_doctype):
|
|||||||
obj.flags.ignore_validate = True
|
obj.flags.ignore_validate = True
|
||||||
obj.set_title_field()
|
obj.set_title_field()
|
||||||
obj.insert(ignore_mandatory=True)
|
obj.insert(ignore_mandatory=True)
|
||||||
|
del frappe.flags.bulk_transaction
|
||||||
|
|
||||||
|
|
||||||
def check_logger_doc_exists(log_date):
|
def create_log(doc_name, e, from_doctype, to_doctype, status, log_date=None, restarted=0):
|
||||||
return frappe.db.exists("Bulk Transaction Log", log_date)
|
transaction_log = frappe.new_doc("Bulk Transaction Log Detail")
|
||||||
|
transaction_log.transaction_name = doc_name
|
||||||
|
transaction_log.date = today()
|
||||||
def get_logger_doc(log_date):
|
|
||||||
return frappe.get_doc("Bulk Transaction Log", log_date)
|
|
||||||
|
|
||||||
|
|
||||||
def create_logger_doc():
|
|
||||||
log_doc = frappe.new_doc("Bulk Transaction Log")
|
|
||||||
log_doc.set_new_name(set_name=str(date.today()))
|
|
||||||
log_doc.log_date = date.today()
|
|
||||||
|
|
||||||
return log_doc
|
|
||||||
|
|
||||||
|
|
||||||
def append_data_to_logger(log_doc, doc_name, error, from_doctype, to_doctype, status, restarted):
|
|
||||||
row = log_doc.append("logger_data", {})
|
|
||||||
row.transaction_name = doc_name
|
|
||||||
row.date = date.today()
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
row.time = now.strftime("%H:%M:%S")
|
transaction_log.time = now.strftime("%H:%M:%S")
|
||||||
row.transaction_status = status
|
transaction_log.transaction_status = status
|
||||||
row.error_description = str(error)
|
transaction_log.error_description = str(e)
|
||||||
row.from_doctype = from_doctype
|
transaction_log.from_doctype = from_doctype
|
||||||
row.to_doctype = to_doctype
|
transaction_log.to_doctype = to_doctype
|
||||||
row.retried = restarted
|
transaction_log.retried = restarted
|
||||||
|
transaction_log.save()
|
||||||
|
|
||||||
def update_logger(doc_name, e, from_doctype, to_doctype, status, log_date=None, restarted=0):
|
|
||||||
if not check_logger_doc_exists(log_date):
|
|
||||||
log_doc = create_logger_doc()
|
|
||||||
append_data_to_logger(log_doc, doc_name, e, from_doctype, to_doctype, status, restarted)
|
|
||||||
log_doc.insert()
|
|
||||||
else:
|
|
||||||
log_doc = get_logger_doc(log_date)
|
|
||||||
if record_exists(log_doc, doc_name, status):
|
|
||||||
append_data_to_logger(log_doc, doc_name, e, from_doctype, to_doctype, status, restarted)
|
|
||||||
log_doc.save()
|
|
||||||
|
|
||||||
|
|
||||||
def show_job_status(fail_count, deserialized_data_count, to_doctype):
|
def show_job_status(fail_count, deserialized_data_count, to_doctype):
|
||||||
@@ -176,23 +194,3 @@ def show_job_status(fail_count, deserialized_data_count, to_doctype):
|
|||||||
title="Failed",
|
title="Failed",
|
||||||
indicator="red",
|
indicator="red",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def record_exists(log_doc, doc_name, status):
|
|
||||||
record = mark_retrired_transaction(log_doc, doc_name)
|
|
||||||
if record and status == "Failed":
|
|
||||||
return False
|
|
||||||
elif record and status == "Success":
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def mark_retrired_transaction(log_doc, doc_name):
|
|
||||||
record = 0
|
|
||||||
for d in log_doc.get("logger_data"):
|
|
||||||
if d.transaction_name == doc_name and d.transaction_status == "Failed":
|
|
||||||
frappe.db.set_value("Bulk Transaction Log Detail", d.name, "retried", 1)
|
|
||||||
record = record + 1
|
|
||||||
|
|
||||||
return record
|
|
||||||
|
|||||||
Reference in New Issue
Block a user