Merge pull request #35828 from phot0n/pr-fieldname-item-wise-purchase-register
fix: use correct fieldname for purchase receipt column in item_wise_purchase_register report
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index d5f0052..e2d8957 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -43,9 +43,11 @@
fi
- name: Setup Python
- uses: "gabrielfalcao/pyenv-action@v9"
+ uses: "actions/setup-python@v4"
with:
- versions: 3.10:latest, 3.7:latest
+ python-version: |
+ 3.7
+ 3.10
- name: Setup Node
uses: actions/setup-node@v2
@@ -92,7 +94,6 @@
- name: Install
run: |
pip install frappe-bench
- pyenv global $(pyenv versions | grep '3.10')
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
@@ -107,7 +108,6 @@
git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
- pyenv global $(pyenv versions | grep '3.7')
for version in $(seq 12 13)
do
echo "Updating to v$version"
@@ -120,7 +120,7 @@
git -C "apps/erpnext" checkout -q -f $branch_name
rm -rf ~/frappe-bench/env
- bench setup env
+ bench setup env --python python3.7
bench pip install -e ./apps/payments
bench pip install -e ./apps/erpnext
@@ -132,9 +132,8 @@
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
- pyenv global $(pyenv versions | grep '3.10')
rm -rf ~/frappe-bench/env
- bench -v setup env
+ bench -v setup env --python python3.10
bench pip install -e ./apps/payments
bench pip install -e ./apps/erpnext
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index c4a23a6..0eef3e9 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -10,6 +10,7 @@
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import cint, flt
+from erpnext import get_default_cost_center
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
get_amounts_not_reflected_in_system,
@@ -140,6 +141,9 @@
second_account
)
)
+
+ company = frappe.get_value("Account", company_account, "company")
+
accounts = []
# Multi Currency?
accounts.append(
@@ -149,6 +153,7 @@
"debit_in_account_currency": bank_transaction.withdrawal,
"party_type": party_type,
"party": party,
+ "cost_center": get_default_cost_center(company),
}
)
@@ -158,11 +163,10 @@
"bank_account": bank_transaction.bank_account,
"credit_in_account_currency": bank_transaction.withdrawal,
"debit_in_account_currency": bank_transaction.deposit,
+ "cost_center": get_default_cost_center(company),
}
)
- company = frappe.get_value("Account", company_account, "company")
-
journal_entry_dict = {
"voucher_type": entry_type,
"company": company,
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
index 0a7d057..fd2d931 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
@@ -73,6 +73,7 @@
"fieldname": "current_exchange_rate",
"fieldtype": "Float",
"label": "Current Exchange Rate",
+ "precision": "9",
"read_only": 1
},
{
@@ -148,7 +149,7 @@
],
"istable": 1,
"links": [],
- "modified": "2023-06-20 07:21:40.743460",
+ "modified": "2023-06-22 12:39:56.446722",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Exchange Rate Revaluation Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 9f55ba1..bac84db 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -155,6 +155,7 @@
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
frm.events.show_general_ledger(frm);
+ erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm);
},
validate_company: (frm) => {
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 2283677..89fa151 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -85,25 +85,29 @@
// check for any running reconciliation jobs
if (this.frm.doc.receivable_payable_account) {
- frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments").then((enabled) => {
- if(enabled) {
- this.frm.call({
- 'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.is_any_doc_running",
- "args": {
- for_filter: {
- company: this.frm.doc.company,
- party_type: this.frm.doc.party_type,
- party: this.frm.doc.party,
- receivable_payable_account: this.frm.doc.receivable_payable_account
+ this.frm.call({
+ doc: this.frm.doc,
+ method: 'is_auto_process_enabled',
+ callback: (r) => {
+ if (r.message) {
+ this.frm.call({
+ 'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.is_any_doc_running",
+ "args": {
+ for_filter: {
+ company: this.frm.doc.company,
+ party_type: this.frm.doc.party_type,
+ party: this.frm.doc.party,
+ receivable_payable_account: this.frm.doc.receivable_payable_account
+ }
}
- }
- }).then(r => {
- if (r.message) {
- let doc_link = frappe.utils.get_form_link("Process Payment Reconciliation", r.message, true);
- let msg = __("Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.", [doc_link]);
- this.frm.dashboard.add_comment(msg, "yellow");
- }
- });
+ }).then(r => {
+ if (r.message) {
+ let doc_link = frappe.utils.get_form_link("Process Payment Reconciliation", r.message, true);
+ let msg = __("Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.", [doc_link]);
+ this.frm.dashboard.add_comment(msg, "yellow");
+ }
+ });
+ }
}
});
}
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 2c8faec..2e4e3b0 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -253,6 +253,10 @@
return difference_amount
@frappe.whitelist()
+ def is_auto_process_enabled(self):
+ return frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments")
+
+ @frappe.whitelist()
def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount):
invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index ab7884d..6a558ca 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -54,9 +54,11 @@
hide_fields(this.frm.doc);
// Show / Hide button
this.show_general_ledger();
+ erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
- if(doc.update_stock==1 && doc.docstatus==1) {
+ if(doc.update_stock==1) {
this.show_stock_ledger();
+ erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
}
if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8cb2950..68407e0 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -88,8 +88,12 @@
}
this.show_general_ledger();
+ erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
- if(doc.update_stock) this.show_stock_ledger();
+ if(doc.update_stock){
+ this.show_stock_ledger();
+ erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
+ }
if (doc.docstatus == 1 && doc.outstanding_amount!=0
&& !(cint(doc.is_return) && doc.return_against)) {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index ab4aab3..f0d3f72 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -2157,7 +2157,7 @@
"link_fieldname": "consolidated_invoice"
}
],
- "modified": "2023-06-19 16:02:05.309332",
+ "modified": "2023-06-21 16:02:18.988799",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
index 01fcb11..6d55d77 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
@@ -15,7 +15,6 @@
}
refresh() {
- erpnext.hide_company();
this.show_general_ledger();
if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) {
this.show_stock_ledger();
@@ -129,10 +128,6 @@
return this.get_target_item_details();
}
- target_asset() {
- return this.get_target_asset_details();
- }
-
item_code(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
if (cdt === "Asset Capitalization Stock Item") {
@@ -247,26 +242,6 @@
}
}
- get_target_asset_details() {
- var me = this;
-
- if (me.frm.doc.target_asset) {
- return me.frm.call({
- method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_asset_details",
- child: me.frm.doc,
- args: {
- asset: me.frm.doc.target_asset,
- company: me.frm.doc.company,
- },
- callback: function (r) {
- if (!r.exc) {
- me.frm.refresh_fields();
- }
- }
- });
- }
- }
-
get_consumed_stock_item_details(row) {
var me = this;
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
index 01b35f6..04b0c4e 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
@@ -11,13 +11,14 @@
"naming_series",
"entry_type",
"target_item_code",
+ "target_asset",
"target_item_name",
"target_is_fixed_asset",
"target_has_batch_no",
"target_has_serial_no",
"column_break_9",
- "target_asset",
"target_asset_name",
+ "target_asset_location",
"target_warehouse",
"target_qty",
"target_stock_uom",
@@ -85,14 +86,13 @@
"fieldtype": "Column Break"
},
{
- "depends_on": "eval:doc.entry_type=='Capitalization'",
"fieldname": "target_asset",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Target Asset",
- "mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
"no_copy": 1,
- "options": "Asset"
+ "options": "Asset",
+ "read_only": 1
},
{
"depends_on": "eval:doc.entry_type=='Capitalization'",
@@ -108,11 +108,11 @@
"fieldtype": "Column Break"
},
{
- "fetch_from": "asset.company",
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
+ "remember_last_selected_value": 1,
"reqd": 1
},
{
@@ -158,7 +158,7 @@
"read_only": 1
},
{
- "depends_on": "eval:doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length)",
+ "depends_on": "eval:doc.entry_type=='Capitalization' && (doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length))",
"fieldname": "section_break_16",
"fieldtype": "Section Break",
"label": "Consumed Stock Items"
@@ -189,7 +189,7 @@
"fieldname": "target_qty",
"fieldtype": "Float",
"label": "Target Qty",
- "read_only_depends_on": "target_is_fixed_asset"
+ "read_only_depends_on": "eval:doc.entry_type=='Capitalization'"
},
{
"fetch_from": "target_item_code.stock_uom",
@@ -227,7 +227,7 @@
"depends_on": "eval:doc.docstatus == 0 || (doc.asset_items && doc.asset_items.length)",
"fieldname": "section_break_26",
"fieldtype": "Section Break",
- "label": "Consumed Asset Items"
+ "label": "Consumed Assets"
},
{
"fieldname": "asset_items",
@@ -266,7 +266,7 @@
"options": "Finance Book"
},
{
- "depends_on": "eval:doc.docstatus == 0 || (doc.service_items && doc.service_items.length)",
+ "depends_on": "eval:doc.entry_type=='Capitalization' && (doc.docstatus == 0 || (doc.service_items && doc.service_items.length))",
"fieldname": "service_expenses_section",
"fieldtype": "Section Break",
"label": "Service Expenses"
@@ -329,12 +329,20 @@
"label": "Target Fixed Asset Account",
"options": "Account",
"read_only": 1
+ },
+ {
+ "depends_on": "eval:doc.entry_type=='Capitalization'",
+ "fieldname": "target_asset_location",
+ "fieldtype": "Link",
+ "label": "Target Asset Location",
+ "mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
+ "options": "Location"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-10-12 15:09:40.771332",
+ "modified": "2023-06-22 14:17:07.995120",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Capitalization",
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 6841c56..a883bec 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -19,9 +19,6 @@
reverse_depreciation_entry_made_after_disposal,
)
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
-from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
- make_new_active_asset_depr_schedules_and_cancel_current_ones,
-)
from erpnext.controllers.stock_controller import StockController
from erpnext.setup.doctype.brand.brand import get_brand_defaults
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@@ -45,7 +42,6 @@
"target_has_batch_no",
"target_stock_uom",
"stock_uom",
- "target_fixed_asset_account",
"fixed_asset_account",
"valuation_rate",
]
@@ -56,7 +52,6 @@
self.validate_posting_time()
self.set_missing_values(for_validate=True)
self.validate_target_item()
- self.validate_target_asset()
self.validate_consumed_stock_item()
self.validate_consumed_asset_item()
self.validate_service_item()
@@ -71,11 +66,12 @@
def before_submit(self):
self.validate_source_mandatory()
+ if self.entry_type == "Capitalization":
+ self.create_target_asset()
def on_submit(self):
self.update_stock_ledger()
self.make_gl_entries()
- self.update_target_asset()
def on_cancel(self):
self.ignore_linked_doctypes = (
@@ -86,7 +82,7 @@
)
self.update_stock_ledger()
self.make_gl_entries()
- self.update_target_asset()
+ self.restore_consumed_asset_items()
def set_title(self):
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
@@ -97,15 +93,6 @@
if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
self.set(k, v)
- # Remove asset if item not a fixed asset
- if not self.target_is_fixed_asset:
- self.target_asset = None
-
- target_asset_details = get_target_asset_details(self.target_asset, self.company)
- for k, v in target_asset_details.items():
- if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
- self.set(k, v)
-
for d in self.stock_items:
args = self.as_dict()
args.update(d.as_dict())
@@ -157,9 +144,6 @@
if not target_item.is_stock_item:
self.target_warehouse = None
- if not target_item.is_fixed_asset:
- self.target_asset = None
- self.target_fixed_asset_account = None
if not target_item.has_batch_no:
self.target_batch_no = None
if not target_item.has_serial_no:
@@ -170,17 +154,6 @@
self.validate_item(target_item)
- def validate_target_asset(self):
- if self.target_asset:
- target_asset = self.get_asset_for_validation(self.target_asset)
-
- if target_asset.item_code != self.target_item_code:
- frappe.throw(
- _("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code)
- )
-
- self.validate_asset(target_asset)
-
def validate_consumed_stock_item(self):
for d in self.stock_items:
if d.item_code:
@@ -386,7 +359,11 @@
gl_entries, target_account, target_against, precision
)
+ if not self.stock_items and not self.service_items and self.are_all_asset_items_non_depreciable:
+ return []
+
self.get_gl_entries_for_target_item(gl_entries, target_against, precision)
+
return gl_entries
def get_target_account(self):
@@ -429,11 +406,14 @@
def get_gl_entries_for_consumed_asset_items(
self, gl_entries, target_account, target_against, precision
):
+ self.are_all_asset_items_non_depreciable = True
+
# Consumed Assets
for item in self.asset_items:
- asset = self.get_asset(item)
+ asset = frappe.get_doc("Asset", item.asset)
if asset.calculate_depreciation:
+ self.are_all_asset_items_non_depreciable = False
notes = _(
"This schedule was created when Asset {0} was consumed through Asset Capitalization {1}."
).format(
@@ -519,40 +499,46 @@
)
)
- def update_target_asset(self):
+ def create_target_asset(self):
total_target_asset_value = flt(self.total_value, self.precision("total_value"))
- if self.docstatus == 1 and self.entry_type == "Capitalization":
- asset_doc = frappe.get_doc("Asset", self.target_asset)
- asset_doc.purchase_date = self.posting_date
- asset_doc.gross_purchase_amount = total_target_asset_value
- asset_doc.purchase_receipt_amount = total_target_asset_value
- notes = _(
- "This schedule was created when target Asset {0} was updated through Asset Capitalization {1}."
- ).format(
- get_link_to_form(asset_doc.doctype, asset_doc.name), get_link_to_form(self.doctype, self.name)
- )
- make_new_active_asset_depr_schedules_and_cancel_current_ones(asset_doc, notes)
- asset_doc.flags.ignore_validate_update_after_submit = True
- asset_doc.save()
- elif self.docstatus == 2:
- for item in self.asset_items:
- asset = self.get_asset(item)
- asset.db_set("disposal_date", None)
- self.set_consumed_asset_status(asset)
+ asset_doc = frappe.new_doc("Asset")
+ asset_doc.company = self.company
+ asset_doc.item_code = self.target_item_code
+ asset_doc.is_existing_asset = 1
+ asset_doc.location = self.target_asset_location
+ asset_doc.available_for_use_date = self.posting_date
+ asset_doc.purchase_date = self.posting_date
+ asset_doc.gross_purchase_amount = total_target_asset_value
+ asset_doc.purchase_receipt_amount = total_target_asset_value
+ asset_doc.flags.ignore_validate = True
+ asset_doc.insert()
- if asset.calculate_depreciation:
- reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
- notes = _(
- "This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation."
- ).format(
- get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.name)
- )
- reset_depreciation_schedule(asset, self.posting_date, notes)
+ self.target_asset = asset_doc.name
- def get_asset(self, item):
- asset = frappe.get_doc("Asset", item.asset)
- self.check_finance_books(item, asset)
- return asset
+ self.target_fixed_asset_account = get_asset_category_account(
+ "fixed_asset_account", item=self.target_item_code, company=asset_doc.company
+ )
+
+ frappe.msgprint(
+ _(
+ "Asset {0} has been created. Please set the depreciation details if any and submit it."
+ ).format(get_link_to_form("Asset", asset_doc.name))
+ )
+
+ def restore_consumed_asset_items(self):
+ for item in self.asset_items:
+ asset = frappe.get_doc("Asset", item.asset)
+ asset.db_set("disposal_date", None)
+ self.set_consumed_asset_status(asset)
+
+ if asset.calculate_depreciation:
+ reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
+ notes = _(
+ "This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation."
+ ).format(
+ get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.name)
+ )
+ reset_depreciation_schedule(asset, self.posting_date, notes)
def set_consumed_asset_status(self, asset):
if self.docstatus == 1:
@@ -603,33 +589,6 @@
@frappe.whitelist()
-def get_target_asset_details(asset=None, company=None):
- out = frappe._dict()
-
- # Get Asset Details
- asset_details = frappe._dict()
- if asset:
- asset_details = frappe.db.get_value("Asset", asset, ["asset_name", "item_code"], as_dict=1)
- if not asset_details:
- frappe.throw(_("Asset {0} does not exist").format(asset))
-
- # Re-set item code from Asset
- out.target_item_code = asset_details.item_code
-
- # Set Asset Details
- out.asset_name = asset_details.asset_name
-
- if asset_details.item_code:
- out.target_fixed_asset_account = get_asset_category_account(
- "fixed_asset_account", item=asset_details.item_code, company=company
- )
- else:
- out.target_fixed_asset_account = None
-
- return out
-
-
-@frappe.whitelist()
def get_consumed_stock_item_details(args):
if isinstance(args, str):
args = json.loads(args)
diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
index 5345d0e..6e0a685 100644
--- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
@@ -47,13 +47,6 @@
total_amount = 103000
- # Create assets
- target_asset = create_asset(
- asset_name="Asset Capitalization Target Asset",
- submit=1,
- warehouse="Stores - TCP1",
- company=company,
- )
consumed_asset = create_asset(
asset_name="Asset Capitalization Consumable Asset",
asset_value=consumed_asset_value,
@@ -65,7 +58,8 @@
# Create and submit Asset Captitalization
asset_capitalization = create_asset_capitalization(
entry_type="Capitalization",
- target_asset=target_asset.name,
+ target_item_code="Macbook Pro",
+ target_asset_location="Test Location",
stock_qty=stock_qty,
stock_rate=stock_rate,
consumed_asset=consumed_asset.name,
@@ -94,7 +88,7 @@
self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
# Test Target Asset values
- target_asset.reload()
+ target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
@@ -142,13 +136,6 @@
total_amount = 103000
- # Create assets
- target_asset = create_asset(
- asset_name="Asset Capitalization Target Asset",
- submit=1,
- warehouse="Stores - _TC",
- company=company,
- )
consumed_asset = create_asset(
asset_name="Asset Capitalization Consumable Asset",
asset_value=consumed_asset_value,
@@ -160,7 +147,8 @@
# Create and submit Asset Captitalization
asset_capitalization = create_asset_capitalization(
entry_type="Capitalization",
- target_asset=target_asset.name,
+ target_item_code="Macbook Pro",
+ target_asset_location="Test Location",
stock_qty=stock_qty,
stock_rate=stock_rate,
consumed_asset=consumed_asset.name,
@@ -189,7 +177,7 @@
self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
# Test Target Asset values
- target_asset.reload()
+ target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
@@ -364,6 +352,7 @@
"posting_time": args.posting_time or now.strftime("%H:%M:%S.%f"),
"target_item_code": target_item_code,
"target_asset": target_asset.name,
+ "target_asset_location": "Test Location",
"target_warehouse": target_warehouse,
"target_qty": flt(args.target_qty) or 1,
"target_batch_no": args.target_batch_no,
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 818c789..9546680 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -320,7 +320,9 @@
return data[0]
-def make_return_doc(doctype: str, source_name: str, target_doc=None):
+def make_return_doc(
+ doctype: str, source_name: str, target_doc=None, return_against_rejected_qty=False
+):
from frappe.model.mapper import get_mapped_doc
company = frappe.db.get_value("Delivery Note", source_name, "company")
@@ -471,7 +473,7 @@
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
- if hasattr(target_doc, "stock_qty"):
+ if hasattr(target_doc, "stock_qty") and not return_against_rejected_qty:
target_doc.stock_qty = -1 * flt(
source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0)
)
@@ -490,6 +492,13 @@
target_doc.rejected_warehouse = source_doc.rejected_warehouse
target_doc.purchase_receipt_item = source_doc.name
+ if doctype == "Purchase Receipt" and return_against_rejected_qty:
+ target_doc.qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get("qty") or 0))
+ target_doc.rejected_qty = 0.0
+ target_doc.rejected_warehouse = ""
+ target_doc.warehouse = source_doc.rejected_warehouse
+ target_doc.received_qty = target_doc.qty
+
elif doctype == "Purchase Invoice":
returned_qty_map = get_returned_qty_map_for_row(
source_parent.name, source_parent.supplier, source_doc.name, doctype
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index cdbf6c7..5137e03 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -845,6 +845,149 @@
gl_entries.append(self.get_gl_dict(gl_entry, item=item))
+@frappe.whitelist()
+def show_accounting_ledger_preview(company, doctype, docname):
+ filters = {"company": company, "include_dimensions": 1}
+ doc = frappe.get_doc(doctype, docname)
+
+ gl_columns, gl_data = get_accounting_ledger_preview(doc, filters)
+
+ frappe.db.rollback()
+
+ return {"gl_columns": gl_columns, "gl_data": gl_data}
+
+
+@frappe.whitelist()
+def show_stock_ledger_preview(company, doctype, docname):
+ filters = {"company": company}
+ doc = frappe.get_doc(doctype, docname)
+
+ sl_columns, sl_data = get_stock_ledger_preview(doc, filters)
+
+ frappe.db.rollback()
+
+ return {
+ "sl_columns": sl_columns,
+ "sl_data": sl_data,
+ }
+
+
+def get_accounting_ledger_preview(doc, filters):
+ from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns
+
+ gl_columns, gl_data = [], []
+ fields = [
+ "posting_date",
+ "account",
+ "debit",
+ "credit",
+ "against",
+ "party",
+ "party_type",
+ "cost_center",
+ "against_voucher_type",
+ "against_voucher",
+ ]
+
+ doc.docstatus = 1
+
+ if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"):
+ doc.update_stock_ledger()
+
+ doc.make_gl_entries()
+ columns = get_gl_columns(filters)
+ gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields)
+
+ gl_columns = get_columns(columns, fields)
+ gl_data = get_data(fields, gl_entries)
+
+ return gl_columns, gl_data
+
+
+def get_stock_ledger_preview(doc, filters):
+ from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns
+
+ sl_columns, sl_data = [], []
+ fields = [
+ "item_code",
+ "stock_uom",
+ "actual_qty",
+ "qty_after_transaction",
+ "warehouse",
+ "incoming_rate",
+ "valuation_rate",
+ "stock_value",
+ "stock_value_difference",
+ ]
+ columns_fields = [
+ "item_code",
+ "stock_uom",
+ "in_qty",
+ "out_qty",
+ "qty_after_transaction",
+ "warehouse",
+ "incoming_rate",
+ "in_out_rate",
+ "stock_value",
+ "stock_value_difference",
+ ]
+
+ if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"):
+ doc.docstatus = 1
+ doc.update_stock_ledger()
+ columns = get_sl_columns(filters)
+ sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields)
+
+ sl_columns = get_columns(columns, columns_fields)
+ sl_data = get_data(columns_fields, sl_entries)
+
+ return sl_columns, sl_data
+
+
+def get_sl_entries_for_preview(doctype, docname, fields):
+ sl_entries = frappe.get_all(
+ "Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields
+ )
+
+ for entry in sl_entries:
+ if entry.actual_qty > 0:
+ entry["in_qty"] = entry.actual_qty
+ entry["out_qty"] = 0
+ else:
+ entry["out_qty"] = abs(entry.actual_qty)
+ entry["in_qty"] = 0
+
+ entry["in_out_rate"] = entry["valuation_rate"]
+
+ return sl_entries
+
+
+def get_gl_entries_for_preview(doctype, docname, fields):
+ return frappe.get_all(
+ "GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields
+ )
+
+
+def get_columns(raw_columns, fields):
+ return [
+ {"name": d.get("label"), "editable": False, "width": 110}
+ for d in raw_columns
+ if not d.get("hidden") and d.get("fieldname") in fields
+ ]
+
+
+def get_data(raw_columns, raw_data):
+ datatable_data = []
+ for row in raw_data:
+ data_row = []
+ for column in raw_columns:
+ data_row.append(row.get(column) or "")
+
+ datatable_data.append(data_row)
+
+ return datatable_data
+
+
def repost_required_for_queue(doc: StockController) -> bool:
"""check if stock document contains repeated item-warehouse with queue based valuation.
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index d346357..720423b 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -66,7 +66,7 @@
}
show_general_ledger() {
- var me = this;
+ let me = this;
if(this.frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index cc020fc..4e028e4 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -17,6 +17,7 @@
import "./utils/supplier_quick_entry";
import "./call_popup/call_popup";
import "./utils/dimension_tree_filter";
+import "./utils/ledger_preview.js"
import "./utils/barcode_scanner";
import "./telephony";
import "./templates/call_link.html";
diff --git a/erpnext/public/js/utils/ledger_preview.js b/erpnext/public/js/utils/ledger_preview.js
new file mode 100644
index 0000000..85d4a7d
--- /dev/null
+++ b/erpnext/public/js/utils/ledger_preview.js
@@ -0,0 +1,78 @@
+frappe.provide('erpnext.accounts');
+
+erpnext.accounts.ledger_preview = {
+ show_accounting_ledger_preview(frm) {
+ let me = this;
+ if(!frm.is_new() && frm.doc.docstatus == 0) {
+ frm.add_custom_button(__('Accounting Ledger'), function() {
+ frappe.call({
+ "type": "GET",
+ "method": "erpnext.controllers.stock_controller.show_accounting_ledger_preview",
+ "args": {
+ "company": frm.doc.company,
+ "doctype": frm.doc.doctype,
+ "docname": frm.doc.name
+ },
+ "callback": function(response) {
+ me.make_dialog("Accounting Ledger Preview", "accounting_ledger_preview_html", response.message.gl_columns, response.message.gl_data);
+ }
+ })
+ }, __("Preview"));
+ }
+ },
+
+ show_stock_ledger_preview(frm) {
+ let me = this
+ if(!frm.is_new() && frm.doc.docstatus == 0) {
+ frm.add_custom_button(__('Stock Ledger'), function() {
+ frappe.call({
+ "type": "GET",
+ "method": "erpnext.controllers.stock_controller.show_stock_ledger_preview",
+ "args": {
+ "company": frm.doc.company,
+ "doctype": frm.doc.doctype,
+ "docname": frm.doc.name
+ },
+ "callback": function(response) {
+ me.make_dialog("Stock Ledger Preview", "stock_ledger_preview_html", response.message.sl_columns, response.message.sl_data);
+ }
+ })
+ }, __("Preview"));
+ }
+ },
+
+ make_dialog(label, fieldname, columns, data) {
+ let me = this;
+ let dialog = new frappe.ui.Dialog({
+ "size": "extra-large",
+ "title": __(label),
+ "fields": [
+ {
+ "fieldtype": "HTML",
+ "fieldname": fieldname,
+ },
+ ]
+ });
+
+ setTimeout(function() {
+ me.get_datatable(columns, data, dialog.get_field(fieldname).wrapper);
+ }, 200);
+
+ dialog.show();
+ },
+
+ get_datatable(columns, data, wrapper) {
+ const datatable_options = {
+ columns: columns,
+ data: data,
+ dynamicRowHeight: true,
+ checkboxColumn: false,
+ inlineFilters: true,
+ };
+
+ new frappe.DataTable(
+ wrapper,
+ datatable_options
+ );
+ }
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 77545e0..a648195 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -200,6 +200,9 @@
}
}
+ erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
+ erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
+
if (doc.docstatus > 0) {
this.show_stock_ledger();
if (erpnext.is_perpetual_inventory_enabled(doc.company)) {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 312c166..35aad78 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -121,6 +121,10 @@
refresh() {
var me = this;
super.refresh();
+
+ erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
+ erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
+
if(this.frm.doc.docstatus > 0) {
this.show_stock_ledger();
//removed for temporary
@@ -209,10 +213,43 @@
}
make_purchase_return() {
- frappe.model.open_mapped_doc({
- method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",
- frm: cur_frm
+ let me = this;
+
+ let has_rejected_items = cur_frm.doc.items.filter((item) => {
+ if (item.rejected_qty > 0) {
+ return true;
+ }
})
+
+ if (has_rejected_items && has_rejected_items.length > 0) {
+ frappe.prompt([
+ {
+ label: __("Return Qty from Rejected Warehouse"),
+ fieldtype: "Check",
+ fieldname: "return_for_rejected_warehouse",
+ default: 1
+ },
+ ], function(values){
+ if (values.return_for_rejected_warehouse) {
+ frappe.call({
+ method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return_against_rejected_warehouse",
+ args: {
+ source_name: cur_frm.doc.name
+ },
+ callback: function(r) {
+ if(r.message) {
+ frappe.model.sync(r.message);
+ frappe.set_route("Form", r.message.doctype, r.message.name);
+ }
+ }
+ })
+ } else {
+ cur_frm.cscript._make_purchase_return();
+ }
+ }, __("Return Qty"), __("Make Return Entry"));
+ } else {
+ cur_frm.cscript._make_purchase_return();
+ }
}
close_purchase_receipt() {
@@ -322,6 +359,13 @@
},
});
+cur_frm.cscript._make_purchase_return = function() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",
+ frm: cur_frm
+ });
+}
+
cur_frm.cscript['Make Stock Entry'] = function() {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_stock_entry",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 387f031..0b5dc05 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -1137,6 +1137,13 @@
@frappe.whitelist()
+def make_purchase_return_against_rejected_warehouse(source_name):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+ return make_return_doc("Purchase Receipt", source_name, return_against_rejected_qty=True)
+
+
+@frappe.whitelist()
def make_purchase_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index ddc0556..1986722 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1827,6 +1827,33 @@
self.assertEqual(abs(data["stock_value_difference"]), 400.00)
+ def test_return_from_rejected_warehouse(self):
+ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+ make_purchase_return_against_rejected_warehouse,
+ )
+
+ item_code = "_Test Item Return from Rejected Warehouse"
+ create_item(item_code)
+
+ warehouse = create_warehouse("_Test Warehouse Return Qty Warehouse")
+ rejected_warehouse = create_warehouse("_Test Rejected Warehouse Return Qty Warehouse")
+
+ # Step 1: Create Purchase Receipt with valuation rate 100
+ pr = make_purchase_receipt(
+ item_code=item_code,
+ warehouse=warehouse,
+ qty=10,
+ rate=100,
+ rejected_qty=2,
+ rejected_warehouse=rejected_warehouse,
+ )
+
+ pr_return = make_purchase_return_against_rejected_warehouse(pr.name)
+ self.assertEqual(pr_return.items[0].warehouse, rejected_warehouse)
+ self.assertEqual(pr_return.items[0].qty, 2.0 * -1)
+ self.assertEqual(pr_return.items[0].rejected_qty, 0.0)
+ self.assertEqual(pr_return.items[0].rejected_warehouse, "")
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier