Merge branch 'develop' of https://github.com/frappe/erpnext into asset_purchase_receipt_gl
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index ae54b80..4b2c922 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -73,11 +73,6 @@
gl_entries = self.get_gl_entries(warehouse_account)
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)
- make_gl_entries(gl_entries, from_repost=from_repost)
-
def validate_serialized_batch(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index de0db1a..c2c4d0f 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -144,8 +144,8 @@
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
# check cwip accounts before making auto assets
# 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")
- cwip_account = get_asset_account(
+ self.get_company_default("asset_received_but_not_billed")
+ get_asset_account(
"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
)
break
@@ -313,7 +313,6 @@
self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account)
self.make_tax_gl_entries(gl_entries)
- self.get_asset_gl_entry(gl_entries)
return process_gl_map(gl_entries)
@@ -322,11 +321,19 @@
get_purchase_document_details,
)
- stock_rbnb = None
+ is_asset_pr = any(d.is_fixed_asset for d in self.get("items"))
+ stock_asset_rbnb = None
+ remarks = self.get("remarks") or _("Accounting Entry for {0}").format(
+ "Asset" if is_asset_pr else "Stock"
+ )
+
if erpnext.is_perpetual_inventory_enabled(self.company):
- stock_rbnb = self.get_company_default("stock_received_but_not_billed")
+ stock_asset_rbnb = (
+ self.get_company_default("asset_received_but_not_billed")
+ if is_asset_pr
+ else 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()
@@ -338,236 +345,195 @@
exchange_rate_map, net_rate_map = get_purchase_document_details(self)
- 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 warehouse_account.get(d.warehouse):
- stock_value_diff = frappe.db.get_value(
- "Stock Ledger Entry",
- {
- "voucher_type": "Purchase Receipt",
- "voucher_no": self.name,
- "voucher_detail_no": d.name,
- "warehouse": d.warehouse,
- "is_cancelled": 0,
- },
- "stock_value_difference",
- )
+ def make_item_asset_inward_entries(item, stock_value_diff, stock_asset_account_name):
+ account_currency = get_account_currency(stock_asset_account_name)
+ 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,
+ )
- warehouse_account_name = warehouse_account[d.warehouse]["account"]
- warehouse_account_currency = warehouse_account[d.warehouse]["account_currency"]
- supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account")
- supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get(
- "account_currency"
- )
- remarks = self.get("remarks") or _("Accounting Entry for Stock")
+ 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)
- # If PR is sub-contracted and fg item rate is zero
- # in that case if account for source and target warehouse are same,
- # then GL entries should not be posted
+ outgoing_amount = item.base_net_amount
+ # 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"))
+ )
+
+ 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:
+ 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 * credit_amount,
+ account_currency=account_currency,
+ item=item,
+ )
+
+ # check if the exchange rate has changed
+ if d.get("purchase_invoice"):
if (
- flt(stock_value_diff) == flt(d.rm_supp_cost)
- and warehouse_account.get(self.supplier_warehouse)
- and warehouse_account_name == supplier_warehouse_account
+ 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]
):
- continue
- self.add_gl_entry(
- gl_entries=gl_entries,
- account=warehouse_account_name,
- cost_center=d.cost_center,
- debit=stock_value_diff,
- credit=0.0,
- remarks=remarks,
- against_account=stock_rbnb,
- account_currency=warehouse_account_currency,
- 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",
- )
+ discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * (
+ exchange_rate_map[item.purchase_invoice] - self.conversion_rate
)
- 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,
+ cost_center=item.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,
+ 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,
- project=d.project,
- item=d,
+ item=item,
)
- # 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,
+ account=self.get_company_default("exchange_gain_loss_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,
+ debit=discrepancy_caused_by_exchange_rate_difference,
credit=0.0,
remarks=remarks,
- against_account=warehouse_account_name,
- account_currency=credit_currency,
- project=d.project,
- item=d,
+ against_account=self.supplier,
+ debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
+ account_currency=account_currency,
+ item=item,
)
- elif (
- d.warehouse not in warehouse_with_no_account
- or d.rejected_warehouse not in warehouse_with_no_account
- ):
- warehouse_with_no_account.append(d.warehouse)
- elif (
+ def make_landed_cost_gl_entries(item):
+ # Amount added through landed-cos-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"])
+ )
+
+ 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):
+ if item.is_fixed_asset:
+ return
+
+ # divisional loss adjustment
+ expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
+ valuation_amount_as_per_doc = (
+ flt(item.base_net_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:
+ if self.is_return or flt(item.item_tax_amount):
+ loss_account = expenses_included_in_valuation
+ else:
+ 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,
+ )
+
+ for d in self.get("items"):
+ if (
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")
@@ -575,6 +541,57 @@
self.add_provisional_gl_entry(
d, gl_entries, self.posting_date, d.get("provisional_expense_account")
)
+ elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return):
+ if d.is_fixed_asset:
+ stock_asset_account_name = (
+ get_asset_category_account(
+ asset_category=d.asset_category,
+ fieldname="capital_work_in_progress_account",
+ company=self.company,
+ )
+ if is_cwip_accounting_enabled(d.asset_category)
+ else get_asset_category_account(
+ asset_category=d.asset_category, fieldname="fixed_asset_account", company=self.company
+ )
+ )
+
+ stock_value_diff = flt(d.net_amount) + flt(d.item_tax_amount / self.conversion_rate)
+ elif (
+ (flt(d.valuation_rate) or self.is_return)
+ and flt(d.qty)
+ and 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_currency = warehouse_account.get(self.supplier_warehouse, {}).get(
+ "account_currency"
+ )
+
+ # If PR is sub-contracted and fg item rate is zero
+ # in that case if account for source and target warehouse are same,
+ # then GL entries should not be posted
+ if (
+ flt(stock_value_diff) == flt(d.rm_supp_cost)
+ and warehouse_account.get(self.supplier_warehouse)
+ and stock_asset_account_name == supplier_warehouse_account
+ ):
+ continue
+
+ make_item_asset_inward_entries(d, stock_value_diff, stock_asset_account_name)
+ make_stock_received_but_not_billed_entry(d)
+ make_landed_cost_gl_entries(d)
+ make_rate_difference_entry(d)
+ make_sub_contracting_gl_entries(d)
+ make_divisional_loss_gl_entry(d)
+ elif (
+ d.warehouse not in warehouse_with_no_account
+ or d.rejected_warehouse not in warehouse_with_no_account
+ ):
+ warehouse_with_no_account.append(d.warehouse)
+
+ if d.is_fixed_asset:
+ self.update_assets(d, d.valuation_rate)
if warehouse_with_no_account:
frappe.msgprint(
@@ -700,103 +717,6 @@
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):
assets = frappe.db.get_all(
"Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code}
@@ -830,6 +750,20 @@
self.load_from_db()
+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):
po_billed_amt_details = get_billed_amount_against_po(po_details)
diff --git a/erpnext/templates/pages/integrations/__init__.py b/erpnext/templates/pages/integrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/templates/pages/integrations/__init__.py