mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-04 12:49:10 +00:00
Merge pull request #44741 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
12
.github/helper/install.sh
vendored
12
.github/helper/install.sh
vendored
@@ -6,7 +6,7 @@ cd ~ || exit
|
|||||||
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt remove mysql-server mysql-client
|
sudo apt remove mysql-server mysql-client
|
||||||
sudo apt install libcups2-dev redis-server mariadb-client-10.6
|
sudo apt install libcups2-dev redis-server mariadb-client
|
||||||
|
|
||||||
pip install frappe-bench
|
pip install frappe-bench
|
||||||
|
|
||||||
@@ -44,13 +44,9 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
install_whktml() {
|
install_whktml() {
|
||||||
if [ "$(lsb_release -rs)" = "22.04" ]; then
|
wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
|
||||||
wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
|
sudo apt install /tmp/wkhtmltox.deb
|
||||||
sudo apt install /tmp/wkhtmltox.deb
|
|
||||||
else
|
|
||||||
echo "Please update this script to support wkhtmltopdf for $(lsb_release -ds)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
install_whktml &
|
install_whktml &
|
||||||
wkpid=$!
|
wkpid=$!
|
||||||
|
|||||||
@@ -1328,6 +1328,24 @@ frappe.ui.form.on("Payment Entry", {
|
|||||||
if (r.message) {
|
if (r.message) {
|
||||||
if (!frm.doc.mode_of_payment) {
|
if (!frm.doc.mode_of_payment) {
|
||||||
frm.set_value(field, r.message.account);
|
frm.set_value(field, r.message.account);
|
||||||
|
} else {
|
||||||
|
frappe.call({
|
||||||
|
method: "frappe.client.get_value",
|
||||||
|
args: {
|
||||||
|
doctype: "Mode of Payment Account",
|
||||||
|
filters: {
|
||||||
|
parent: frm.doc.mode_of_payment,
|
||||||
|
company: frm.doc.company,
|
||||||
|
},
|
||||||
|
fieldname: "default_account",
|
||||||
|
parent: "Mode of Payment",
|
||||||
|
},
|
||||||
|
callback: function (res) {
|
||||||
|
if (!res.message.default_account) {
|
||||||
|
frm.set_value(field, r.message.account);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
frm.set_value("bank", r.message.bank);
|
frm.set_value("bank", r.message.bank);
|
||||||
frm.set_value("bank_account_no", r.message.bank_account_no);
|
frm.set_value("bank_account_no", r.message.bank_account_no);
|
||||||
|
|||||||
@@ -117,12 +117,12 @@ class POSInvoiceMergeLog(Document):
|
|||||||
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
|
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
|
||||||
|
|
||||||
sales_invoice, credit_note = "", ""
|
sales_invoice, credit_note = "", ""
|
||||||
if returns:
|
|
||||||
credit_note = self.process_merging_into_credit_note(returns)
|
|
||||||
|
|
||||||
if sales:
|
if sales:
|
||||||
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
||||||
|
|
||||||
|
if returns:
|
||||||
|
credit_note = self.process_merging_into_credit_note(returns, sales_invoice)
|
||||||
|
|
||||||
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
||||||
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
|
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ class POSInvoiceMergeLog(Document):
|
|||||||
self.update_pos_invoices(pos_invoice_docs)
|
self.update_pos_invoices(pos_invoice_docs)
|
||||||
self.serial_and_batch_bundle_reference_for_pos_invoice()
|
self.serial_and_batch_bundle_reference_for_pos_invoice()
|
||||||
self.cancel_linked_invoices()
|
self.cancel_linked_invoices()
|
||||||
|
self.delink_serial_and_batch_bundle()
|
||||||
|
|
||||||
def process_merging_into_sales_invoice(self, data):
|
def process_merging_into_sales_invoice(self, data):
|
||||||
sales_invoice = self.get_new_sales_invoice()
|
sales_invoice = self.get_new_sales_invoice()
|
||||||
@@ -139,8 +140,13 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
sales_invoice.is_consolidated = 1
|
sales_invoice.is_consolidated = 1
|
||||||
sales_invoice.set_posting_time = 1
|
sales_invoice.set_posting_time = 1
|
||||||
sales_invoice.posting_date = getdate(self.posting_date)
|
|
||||||
sales_invoice.posting_time = get_time(self.posting_time)
|
if not sales_invoice.posting_date:
|
||||||
|
sales_invoice.posting_date = getdate(self.posting_date)
|
||||||
|
|
||||||
|
if not sales_invoice.posting_time:
|
||||||
|
sales_invoice.posting_time = get_time(self.posting_time)
|
||||||
|
|
||||||
sales_invoice.save()
|
sales_invoice.save()
|
||||||
sales_invoice.submit()
|
sales_invoice.submit()
|
||||||
|
|
||||||
@@ -148,12 +154,14 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
return sales_invoice.name
|
return sales_invoice.name
|
||||||
|
|
||||||
def process_merging_into_credit_note(self, data):
|
def process_merging_into_credit_note(self, data, sales_invoice):
|
||||||
credit_note = self.get_new_sales_invoice()
|
credit_note = self.get_new_sales_invoice()
|
||||||
credit_note.is_return = 1
|
credit_note.is_return = 1
|
||||||
|
|
||||||
credit_note = self.merge_pos_invoice_into(credit_note, data)
|
credit_note = self.merge_pos_invoice_into(credit_note, data)
|
||||||
|
|
||||||
|
credit_note.return_against = sales_invoice
|
||||||
|
|
||||||
credit_note.is_consolidated = 1
|
credit_note.is_consolidated = 1
|
||||||
credit_note.set_posting_time = 1
|
credit_note.set_posting_time = 1
|
||||||
credit_note.posting_date = getdate(self.posting_date)
|
credit_note.posting_date = getdate(self.posting_date)
|
||||||
@@ -180,6 +188,10 @@ class POSInvoiceMergeLog(Document):
|
|||||||
for doc in data:
|
for doc in data:
|
||||||
map_doc(doc, invoice, table_map={"doctype": invoice.doctype})
|
map_doc(doc, invoice, table_map={"doctype": invoice.doctype})
|
||||||
|
|
||||||
|
if doc.get("posting_date"):
|
||||||
|
invoice.posting_date = getdate(doc.posting_date)
|
||||||
|
invoice.posting_time = get_time(doc.posting_time)
|
||||||
|
|
||||||
if doc.redeem_loyalty_points:
|
if doc.redeem_loyalty_points:
|
||||||
invoice.loyalty_redemption_account = doc.loyalty_redemption_account
|
invoice.loyalty_redemption_account = doc.loyalty_redemption_account
|
||||||
invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
|
invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
|
||||||
@@ -297,6 +309,8 @@ class POSInvoiceMergeLog(Document):
|
|||||||
sales_invoice = frappe.new_doc("Sales Invoice")
|
sales_invoice = frappe.new_doc("Sales Invoice")
|
||||||
sales_invoice.customer = self.customer
|
sales_invoice.customer = self.customer
|
||||||
sales_invoice.is_pos = 1
|
sales_invoice.is_pos = 1
|
||||||
|
sales_invoice.posting_date = None
|
||||||
|
sales_invoice.posting_time = None
|
||||||
|
|
||||||
return sales_invoice
|
return sales_invoice
|
||||||
|
|
||||||
@@ -319,6 +333,38 @@ class POSInvoiceMergeLog(Document):
|
|||||||
for table_name in ["items", "packed_items"]:
|
for table_name in ["items", "packed_items"]:
|
||||||
pos_invoice.set_serial_and_batch_bundle(table_name)
|
pos_invoice.set_serial_and_batch_bundle(table_name)
|
||||||
|
|
||||||
|
def delink_serial_and_batch_bundle(self):
|
||||||
|
bundles = self.get_serial_and_batch_bundles()
|
||||||
|
if not bundles:
|
||||||
|
return
|
||||||
|
|
||||||
|
sle_table = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
query = (
|
||||||
|
frappe.qb.update(sle_table)
|
||||||
|
.set(sle_table.serial_and_batch_bundle, None)
|
||||||
|
.where(sle_table.serial_and_batch_bundle.isin(bundles) & sle_table.is_cancelled == 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
query.run()
|
||||||
|
|
||||||
|
def get_serial_and_batch_bundles(self):
|
||||||
|
pos_invoices = []
|
||||||
|
for d in self.pos_invoices:
|
||||||
|
pos_invoices.append(d.pos_invoice)
|
||||||
|
|
||||||
|
if pos_invoices:
|
||||||
|
return frappe.get_all(
|
||||||
|
"POS Invoice Item",
|
||||||
|
filters={
|
||||||
|
"docstatus": 1,
|
||||||
|
"parent": ["in", pos_invoices],
|
||||||
|
"serial_and_batch_bundle": ["is", "set"],
|
||||||
|
},
|
||||||
|
pluck="serial_and_batch_bundle",
|
||||||
|
)
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def cancel_linked_invoices(self):
|
def cancel_linked_invoices(self):
|
||||||
for si_name in [self.consolidated_invoice, self.consolidated_credit_note]:
|
for si_name in [self.consolidated_invoice, self.consolidated_credit_note]:
|
||||||
if not si_name:
|
if not si_name:
|
||||||
@@ -503,6 +549,9 @@ def cancel_merge_logs(merge_logs, closing_entry=None):
|
|||||||
try:
|
try:
|
||||||
for log in merge_logs:
|
for log in merge_logs:
|
||||||
merge_log = frappe.get_doc("POS Invoice Merge Log", log)
|
merge_log = frappe.get_doc("POS Invoice Merge Log", log)
|
||||||
|
if merge_log.docstatus == 2:
|
||||||
|
continue
|
||||||
|
|
||||||
merge_log.flags.ignore_permissions = True
|
merge_log.flags.ignore_permissions = True
|
||||||
merge_log.cancel()
|
merge_log.cancel()
|
||||||
|
|
||||||
|
|||||||
@@ -415,6 +415,8 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
|
|||||||
"parent": args.parent,
|
"parent": args.parent,
|
||||||
"parenttype": args.parenttype,
|
"parenttype": args.parenttype,
|
||||||
"child_docname": args.get("child_docname"),
|
"child_docname": args.get("child_docname"),
|
||||||
|
"discount_percentage": 0.0,
|
||||||
|
"discount_amount": 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -244,9 +244,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "cc_to",
|
"fieldname": "cc_to",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Table MultiSelect",
|
||||||
"label": "CC To",
|
"label": "CC To",
|
||||||
"options": "User"
|
"options": "Process Statement Of Accounts CC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
@@ -400,7 +400,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-10-18 17:51:39.108481",
|
"modified": "2024-12-11 12:11:13.543134",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Process Statement Of Accounts",
|
"name": "Process Statement Of Accounts",
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ class ProcessStatementOfAccounts(Document):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from frappe.types import DF
|
from frappe.types import DF
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.process_statement_of_accounts_cc.process_statement_of_accounts_cc import (
|
||||||
|
ProcessStatementOfAccountsCC,
|
||||||
|
)
|
||||||
from erpnext.accounts.doctype.process_statement_of_accounts_customer.process_statement_of_accounts_customer import (
|
from erpnext.accounts.doctype.process_statement_of_accounts_customer.process_statement_of_accounts_customer import (
|
||||||
ProcessStatementOfAccountsCustomer,
|
ProcessStatementOfAccountsCustomer,
|
||||||
)
|
)
|
||||||
@@ -41,7 +44,7 @@ class ProcessStatementOfAccounts(Document):
|
|||||||
ageing_based_on: DF.Literal["Due Date", "Posting Date"]
|
ageing_based_on: DF.Literal["Due Date", "Posting Date"]
|
||||||
based_on_payment_terms: DF.Check
|
based_on_payment_terms: DF.Check
|
||||||
body: DF.TextEditor | None
|
body: DF.TextEditor | None
|
||||||
cc_to: DF.Link | None
|
cc_to: DF.TableMultiSelect[ProcessStatementOfAccountsCC]
|
||||||
collection_name: DF.DynamicLink | None
|
collection_name: DF.DynamicLink | None
|
||||||
company: DF.Link
|
company: DF.Link
|
||||||
cost_center: DF.TableMultiSelect[PSOACostCenter]
|
cost_center: DF.TableMultiSelect[PSOACostCenter]
|
||||||
@@ -324,7 +327,7 @@ def get_recipients_and_cc(customer, doc):
|
|||||||
cc = []
|
cc = []
|
||||||
if doc.cc_to != "":
|
if doc.cc_to != "":
|
||||||
try:
|
try:
|
||||||
cc = [frappe.get_value("User", doc.cc_to, "email")]
|
cc = [frappe.get_value("User", user.cc, "email") for user in doc.cc_to]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2024-12-11 12:10:04.654593",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"cc"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "cc",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "CC",
|
||||||
|
"options": "User"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2024-12-11 12:10:39.772598",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Process Statement Of Accounts CC",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "creation",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessStatementOfAccountsCC(Document):
|
||||||
|
# begin: auto-generated types
|
||||||
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from frappe.types import DF
|
||||||
|
|
||||||
|
cc: DF.Link | None
|
||||||
|
parent: DF.Data
|
||||||
|
parentfield: DF.Data
|
||||||
|
parenttype: DF.Data
|
||||||
|
# end: auto-generated types
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -551,9 +551,7 @@ class ReceivablePayableReport:
|
|||||||
self.append_payment_term(row, d, term)
|
self.append_payment_term(row, d, term)
|
||||||
|
|
||||||
def append_payment_term(self, row, d, term):
|
def append_payment_term(self, row, d, term):
|
||||||
if (
|
if d.currency == d.party_account_currency:
|
||||||
self.filters.get("customer") or self.filters.get("supplier")
|
|
||||||
) and d.currency == d.party_account_currency:
|
|
||||||
invoiced = d.payment_amount
|
invoiced = d.payment_amount
|
||||||
else:
|
else:
|
||||||
invoiced = d.base_payment_amount
|
invoiced = d.base_payment_amount
|
||||||
|
|||||||
@@ -28,15 +28,14 @@ def get_group_by_asset_category_data(filters):
|
|||||||
|
|
||||||
for asset_category in asset_categories:
|
for asset_category in asset_categories:
|
||||||
row = frappe._dict()
|
row = frappe._dict()
|
||||||
# row.asset_category = asset_category
|
|
||||||
row.update(asset_category)
|
row.update(asset_category)
|
||||||
|
|
||||||
row.cost_as_on_to_date = (
|
row.value_as_on_to_date = (
|
||||||
flt(row.cost_as_on_from_date)
|
flt(row.value_as_on_from_date)
|
||||||
+ flt(row.cost_of_new_purchase)
|
+ flt(row.value_of_new_purchase)
|
||||||
- flt(row.cost_of_sold_asset)
|
- flt(row.value_of_sold_asset)
|
||||||
- flt(row.cost_of_scrapped_asset)
|
- flt(row.value_of_scrapped_asset)
|
||||||
- flt(row.cost_of_capitalized_asset)
|
- flt(row.value_of_capitalized_asset)
|
||||||
)
|
)
|
||||||
|
|
||||||
row.update(
|
row.update(
|
||||||
@@ -53,11 +52,11 @@ def get_group_by_asset_category_data(filters):
|
|||||||
- flt(row.depreciation_eliminated_during_the_period)
|
- flt(row.depreciation_eliminated_during_the_period)
|
||||||
)
|
)
|
||||||
|
|
||||||
row.net_asset_value_as_on_from_date = flt(row.cost_as_on_from_date) - flt(
|
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
|
||||||
row.accumulated_depreciation_as_on_from_date
|
row.accumulated_depreciation_as_on_from_date
|
||||||
)
|
)
|
||||||
|
|
||||||
row.net_asset_value_as_on_to_date = flt(row.cost_as_on_to_date) - flt(
|
row.net_asset_value_as_on_to_date = flt(row.value_as_on_to_date) - flt(
|
||||||
row.accumulated_depreciation_as_on_to_date
|
row.accumulated_depreciation_as_on_to_date
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,12 +84,12 @@ def get_asset_categories_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_as_on_from_date,
|
end), 0) as value_as_on_from_date,
|
||||||
ifnull(sum(case when a.purchase_date >= %(from_date)s then
|
ifnull(sum(case when a.purchase_date >= %(from_date)s then
|
||||||
a.gross_purchase_amount
|
a.gross_purchase_amount
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_new_purchase,
|
end), 0) as value_of_new_purchase,
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
and a.disposal_date >= %(from_date)s
|
and a.disposal_date >= %(from_date)s
|
||||||
and a.disposal_date <= %(to_date)s then
|
and a.disposal_date <= %(to_date)s then
|
||||||
@@ -101,7 +100,7 @@ def get_asset_categories_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_sold_asset,
|
end), 0) as value_of_sold_asset,
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
and a.disposal_date >= %(from_date)s
|
and a.disposal_date >= %(from_date)s
|
||||||
and a.disposal_date <= %(to_date)s then
|
and a.disposal_date <= %(to_date)s then
|
||||||
@@ -112,7 +111,7 @@ def get_asset_categories_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_scrapped_asset,
|
end), 0) as value_of_scrapped_asset,
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
and a.disposal_date >= %(from_date)s
|
and a.disposal_date >= %(from_date)s
|
||||||
and a.disposal_date <= %(to_date)s then
|
and a.disposal_date <= %(to_date)s then
|
||||||
@@ -123,7 +122,7 @@ def get_asset_categories_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_capitalized_asset
|
end), 0) as value_of_capitalized_asset
|
||||||
from `tabAsset` a
|
from `tabAsset` a
|
||||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
|
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
|
||||||
and not exists(
|
and not exists(
|
||||||
@@ -164,12 +163,12 @@ def get_asset_details_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_as_on_from_date,
|
end), 0) as value_as_on_from_date,
|
||||||
ifnull(sum(case when a.purchase_date >= %(from_date)s then
|
ifnull(sum(case when a.purchase_date >= %(from_date)s then
|
||||||
a.gross_purchase_amount
|
a.gross_purchase_amount
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_new_purchase,
|
end), 0) as value_of_new_purchase,
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
and a.disposal_date >= %(from_date)s
|
and a.disposal_date >= %(from_date)s
|
||||||
and a.disposal_date <= %(to_date)s then
|
and a.disposal_date <= %(to_date)s then
|
||||||
@@ -180,7 +179,7 @@ def get_asset_details_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_sold_asset,
|
end), 0) as value_of_sold_asset,
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
and a.disposal_date >= %(from_date)s
|
and a.disposal_date >= %(from_date)s
|
||||||
and a.disposal_date <= %(to_date)s then
|
and a.disposal_date <= %(to_date)s then
|
||||||
@@ -191,7 +190,7 @@ def get_asset_details_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_scrapped_asset,
|
end), 0) as value_of_scrapped_asset,
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
and a.disposal_date >= %(from_date)s
|
and a.disposal_date >= %(from_date)s
|
||||||
and a.disposal_date <= %(to_date)s then
|
and a.disposal_date <= %(to_date)s then
|
||||||
@@ -202,7 +201,7 @@ def get_asset_details_for_grouped_by_category(filters):
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end), 0) as cost_of_capitalized_asset
|
end), 0) as value_of_capitalized_asset
|
||||||
from `tabAsset` a
|
from `tabAsset` a
|
||||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
|
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition}
|
||||||
and not exists(
|
and not exists(
|
||||||
@@ -232,15 +231,14 @@ def get_group_by_asset_data(filters):
|
|||||||
|
|
||||||
for asset_detail in asset_details:
|
for asset_detail in asset_details:
|
||||||
row = frappe._dict()
|
row = frappe._dict()
|
||||||
# row.asset_category = asset_category
|
|
||||||
row.update(asset_detail)
|
row.update(asset_detail)
|
||||||
|
|
||||||
row.cost_as_on_to_date = (
|
row.value_as_on_to_date = (
|
||||||
flt(row.cost_as_on_from_date)
|
flt(row.value_as_on_from_date)
|
||||||
+ flt(row.cost_of_new_purchase)
|
+ flt(row.value_of_new_purchase)
|
||||||
- flt(row.cost_of_sold_asset)
|
- flt(row.value_of_sold_asset)
|
||||||
- flt(row.cost_of_scrapped_asset)
|
- flt(row.value_of_scrapped_asset)
|
||||||
- flt(row.cost_of_capitalized_asset)
|
- flt(row.value_of_capitalized_asset)
|
||||||
)
|
)
|
||||||
|
|
||||||
row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", "")))
|
row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", "")))
|
||||||
@@ -251,11 +249,11 @@ def get_group_by_asset_data(filters):
|
|||||||
- flt(row.depreciation_eliminated_during_the_period)
|
- flt(row.depreciation_eliminated_during_the_period)
|
||||||
)
|
)
|
||||||
|
|
||||||
row.net_asset_value_as_on_from_date = flt(row.cost_as_on_from_date) - flt(
|
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
|
||||||
row.accumulated_depreciation_as_on_from_date
|
row.accumulated_depreciation_as_on_from_date
|
||||||
)
|
)
|
||||||
|
|
||||||
row.net_asset_value_as_on_to_date = flt(row.cost_as_on_to_date) - flt(
|
row.net_asset_value_as_on_to_date = flt(row.value_as_on_to_date) - flt(
|
||||||
row.accumulated_depreciation_as_on_to_date
|
row.accumulated_depreciation_as_on_to_date
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -446,38 +444,38 @@ def get_columns(filters):
|
|||||||
|
|
||||||
columns += [
|
columns += [
|
||||||
{
|
{
|
||||||
"label": _("Cost as on") + " " + formatdate(filters.day_before_from_date),
|
"label": _("Value as on") + " " + formatdate(filters.day_before_from_date),
|
||||||
"fieldname": "cost_as_on_from_date",
|
"fieldname": "value_as_on_from_date",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 140,
|
"width": 140,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Cost of New Purchase"),
|
"label": _("Value of New Purchase"),
|
||||||
"fieldname": "cost_of_new_purchase",
|
"fieldname": "value_of_new_purchase",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 140,
|
"width": 140,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Cost of Sold Asset"),
|
"label": _("Value of Sold Asset"),
|
||||||
"fieldname": "cost_of_sold_asset",
|
"fieldname": "value_of_sold_asset",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 140,
|
"width": 140,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Cost of Scrapped Asset"),
|
"label": _("Value of Scrapped Asset"),
|
||||||
"fieldname": "cost_of_scrapped_asset",
|
"fieldname": "value_of_scrapped_asset",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 140,
|
"width": 140,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Cost of New Capitalized Asset"),
|
"label": _("Value of New Capitalized Asset"),
|
||||||
"fieldname": "cost_of_capitalized_asset",
|
"fieldname": "value_of_capitalized_asset",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 140,
|
"width": 140,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Cost as on") + " " + formatdate(filters.to_date),
|
"label": _("Value as on") + " " + formatdate(filters.to_date),
|
||||||
"fieldname": "cost_as_on_to_date",
|
"fieldname": "value_as_on_to_date",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 140,
|
"width": 140,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -527,9 +527,16 @@ def get_accounting_entries(
|
|||||||
account_filter_query = get_account_filter_query(root_lft, root_rgt, root_type, gl_entry)
|
account_filter_query = get_account_filter_query(root_lft, root_rgt, root_type, gl_entry)
|
||||||
query = query.where(ExistsCriterion(account_filter_query))
|
query = query.where(ExistsCriterion(account_filter_query))
|
||||||
|
|
||||||
entries = query.run(as_dict=True)
|
from frappe.desk.reportview import build_match_conditions
|
||||||
|
|
||||||
return entries
|
match_conditions = build_match_conditions(doctype)
|
||||||
|
|
||||||
|
if match_conditions:
|
||||||
|
query += "and" + match_conditions
|
||||||
|
|
||||||
|
query, params = query.walk()
|
||||||
|
|
||||||
|
return frappe.db.sql(query, params, as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
def get_account_filter_query(root_lft, root_rgt, root_type, gl_entry):
|
def get_account_filter_query(root_lft, root_rgt, root_type, gl_entry):
|
||||||
|
|||||||
@@ -790,7 +790,10 @@ class GrossProfitGenerator:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if self.filters.group_by == "Sales Person":
|
if self.filters.group_by == "Sales Person":
|
||||||
sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives"
|
sales_person_cols = """, sales.sales_person,
|
||||||
|
sales.allocated_percentage * `tabSales Invoice Item`.base_net_amount / 100 as allocated_amount,
|
||||||
|
sales.incentives
|
||||||
|
"""
|
||||||
sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
|
sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
|
||||||
else:
|
else:
|
||||||
sales_person_cols = ""
|
sales_person_cols = ""
|
||||||
|
|||||||
@@ -804,6 +804,9 @@ class Asset(AccountsController):
|
|||||||
):
|
):
|
||||||
return args.get("rate_of_depreciation")
|
return args.get("rate_of_depreciation")
|
||||||
|
|
||||||
|
if args.get("rate_of_depreciation") and not flt(args.get("expected_value_after_useful_life")):
|
||||||
|
return args.get("rate_of_depreciation")
|
||||||
|
|
||||||
if self.flags.increase_in_asset_value_due_to_repair:
|
if self.flags.increase_in_asset_value_due_to_repair:
|
||||||
value = flt(args.get("expected_value_after_useful_life")) / flt(
|
value = flt(args.get("expected_value_after_useful_life")) / flt(
|
||||||
args.get("value_after_depreciation")
|
args.get("value_after_depreciation")
|
||||||
|
|||||||
@@ -86,7 +86,8 @@
|
|||||||
"description": "In Percentage",
|
"description": "In Percentage",
|
||||||
"fieldname": "rate_of_depreciation",
|
"fieldname": "rate_of_depreciation",
|
||||||
"fieldtype": "Percent",
|
"fieldtype": "Percent",
|
||||||
"label": "Rate of Depreciation"
|
"label": "Rate of Depreciation (%)",
|
||||||
|
"mandatory_depends_on": "eval:doc.depreciation_method == 'Written Down Value'"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "salvage_value_percentage",
|
"fieldname": "salvage_value_percentage",
|
||||||
@@ -117,7 +118,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-05-21 15:48:20.907250",
|
"modified": "2024-12-13 12:11:03.743209",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Finance Book",
|
"name": "Asset Finance Book",
|
||||||
|
|||||||
@@ -76,16 +76,13 @@ def validate_return_against(doc):
|
|||||||
def validate_returned_items(doc):
|
def validate_returned_items(doc):
|
||||||
valid_items = frappe._dict()
|
valid_items = frappe._dict()
|
||||||
|
|
||||||
select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
|
select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor, name"
|
||||||
if doc.doctype != "Purchase Invoice":
|
if doc.doctype != "Purchase Invoice":
|
||||||
select_fields += ",serial_no, batch_no"
|
select_fields += ",serial_no, batch_no"
|
||||||
|
|
||||||
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
|
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
|
||||||
select_fields += ",rejected_qty, received_qty"
|
select_fields += ",rejected_qty, received_qty"
|
||||||
|
|
||||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
|
||||||
select_fields += ",name"
|
|
||||||
|
|
||||||
for d in frappe.db.sql(
|
for d in frappe.db.sql(
|
||||||
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
|
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
|
||||||
doc.return_against,
|
doc.return_against,
|
||||||
@@ -113,11 +110,13 @@ def validate_returned_items(doc):
|
|||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
key = d.item_code
|
key = d.item_code
|
||||||
raise_exception = False
|
raise_exception = False
|
||||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Sales Invoice"]:
|
||||||
field = frappe.scrub(doc.doctype) + "_item"
|
field = frappe.scrub(doc.doctype) + "_item"
|
||||||
if d.get(field):
|
if d.get(field):
|
||||||
key = (d.item_code, d.get(field))
|
key = (d.item_code, d.get(field))
|
||||||
raise_exception = True
|
raise_exception = True
|
||||||
|
elif doc.doctype == "Delivery Note":
|
||||||
|
key = (d.item_code, d.get("dn_detail"))
|
||||||
|
|
||||||
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
|
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
|
||||||
if key not in valid_items:
|
if key not in valid_items:
|
||||||
@@ -129,7 +128,7 @@ def validate_returned_items(doc):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
ref = valid_items.get(key, frappe._dict())
|
ref = valid_items.get(key, frappe._dict())
|
||||||
validate_quantity(doc, d, ref, valid_items, already_returned_items)
|
validate_quantity(doc, key, d, ref, valid_items, already_returned_items)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ref.rate
|
ref.rate
|
||||||
@@ -159,7 +158,7 @@ def validate_returned_items(doc):
|
|||||||
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
||||||
|
|
||||||
|
|
||||||
def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
|
||||||
fields = ["stock_qty"]
|
fields = ["stock_qty"]
|
||||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
|
if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
|
||||||
if not args.get("return_qty_from_rejected_warehouse"):
|
if not args.get("return_qty_from_rejected_warehouse"):
|
||||||
@@ -167,7 +166,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
|||||||
else:
|
else:
|
||||||
fields.extend(["received_qty"])
|
fields.extend(["received_qty"])
|
||||||
|
|
||||||
already_returned_data = already_returned_items.get(args.item_code) or {}
|
already_returned_data = already_returned_items.get(key) or {}
|
||||||
|
|
||||||
company_currency = erpnext.get_company_currency(doc.company)
|
company_currency = erpnext.get_company_currency(doc.company)
|
||||||
stock_qty_precision = get_field_precision(
|
stock_qty_precision = get_field_precision(
|
||||||
@@ -253,15 +252,20 @@ def get_already_returned_items(doc):
|
|||||||
column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
|
column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
|
||||||
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
|
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
|
||||||
|
|
||||||
|
field = (
|
||||||
|
frappe.scrub(doc.doctype) + "_item"
|
||||||
|
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Sales Invoice"]
|
||||||
|
else "dn_detail"
|
||||||
|
)
|
||||||
data = frappe.db.sql(
|
data = frappe.db.sql(
|
||||||
f"""
|
f"""
|
||||||
select {column}
|
select {column}, {field}
|
||||||
from
|
from
|
||||||
`tab{doc.doctype} Item` child, `tab{doc.doctype}` par
|
`tab{doc.doctype} Item` child, `tab{doc.doctype}` par
|
||||||
where
|
where
|
||||||
child.parent = par.name and par.docstatus = 1
|
child.parent = par.name and par.docstatus = 1
|
||||||
and par.is_return = 1 and par.return_against = %s
|
and par.is_return = 1 and par.return_against = %s
|
||||||
group by item_code
|
group by item_code, {field}
|
||||||
""",
|
""",
|
||||||
doc.return_against,
|
doc.return_against,
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
@@ -271,7 +275,7 @@ def get_already_returned_items(doc):
|
|||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
items.setdefault(
|
items.setdefault(
|
||||||
d.item_code,
|
(d.item_code, d.get(field)),
|
||||||
frappe._dict(
|
frappe._dict(
|
||||||
{
|
{
|
||||||
"qty": d.get("qty"),
|
"qty": d.get("qty"),
|
||||||
|
|||||||
@@ -255,15 +255,6 @@ frappe.ui.form.on("BOM", {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skip_qty_field) {
|
|
||||||
fields.push({
|
|
||||||
fieldtype: "Check",
|
|
||||||
label: __("Use Multi-Level BOM"),
|
|
||||||
fieldname: "use_multi_level_bom",
|
|
||||||
default: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!skip_qty_field) {
|
if (!skip_qty_field) {
|
||||||
fields.push({
|
fields.push({
|
||||||
fieldtype: "Float",
|
fieldtype: "Float",
|
||||||
|
|||||||
@@ -385,3 +385,4 @@ erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries
|
|||||||
erpnext.patches.v14_0.update_stock_uom_in_work_order_item
|
erpnext.patches.v14_0.update_stock_uom_in_work_order_item
|
||||||
erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions
|
erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions
|
||||||
erpnext.patches.v15_0.enable_allow_existing_serial_no
|
erpnext.patches.v15_0.enable_allow_existing_serial_no
|
||||||
|
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
process_statement_of_accounts = frappe.qb.DocType("Process Statement Of Accounts")
|
||||||
|
|
||||||
|
data = (
|
||||||
|
frappe.qb.from_(process_statement_of_accounts)
|
||||||
|
.select(process_statement_of_accounts.name, process_statement_of_accounts.cc_to)
|
||||||
|
.where(process_statement_of_accounts.cc_to.isnotnull())
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
for d in data:
|
||||||
|
doc = frappe.get_doc("Process Statement Of Accounts", d.name)
|
||||||
|
doc.append("cc_to", {"cc": d.cc_to})
|
||||||
|
doc.save()
|
||||||
@@ -99,9 +99,12 @@ $.extend(erpnext.queries, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
dispatch_address_query: function (doc) {
|
dispatch_address_query: function (doc) {
|
||||||
|
var filters = { link_doctype: "Company", link_name: doc.company || "" };
|
||||||
|
var is_drop_ship = doc.items.some((item) => item.delivered_by_supplier);
|
||||||
|
if (is_drop_ship) filters = {};
|
||||||
return {
|
return {
|
||||||
query: "frappe.contacts.doctype.address.address.address_query",
|
query: "frappe.contacts.doctype.address.address.address_query",
|
||||||
filters: { link_doctype: "Company", link_name: doc.company || "" },
|
filters: filters,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
|||||||
<div class="right-section">
|
<div class="right-section">
|
||||||
<div class="paid-amount">${format_currency(doc.paid_amount, doc.currency)}</div>
|
<div class="paid-amount">${format_currency(doc.paid_amount, doc.currency)}</div>
|
||||||
<div class="invoice-name">${doc.name}</div>
|
<div class="invoice-name">${doc.name}</div>
|
||||||
<span class="indicator-pill whitespace-nowrap ${indicator_color}"><span>${doc.status}</span></span>
|
<span class="indicator-pill whitespace-nowrap ${indicator_color}"><span>${__(doc.status)}</span></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -505,9 +505,9 @@ class SerialandBatchBundle(Document):
|
|||||||
elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference:
|
elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
d.incoming_rate = rate
|
d.incoming_rate = flt(rate)
|
||||||
if d.qty:
|
if d.qty:
|
||||||
d.stock_value_difference = d.qty * d.incoming_rate
|
d.stock_value_difference = flt(d.qty) * d.incoming_rate
|
||||||
|
|
||||||
if save:
|
if save:
|
||||||
d.db_set(
|
d.db_set(
|
||||||
@@ -519,6 +519,8 @@ class SerialandBatchBundle(Document):
|
|||||||
if not self.voucher_no or self.voucher_no != row.parent:
|
if not self.voucher_no or self.voucher_no != row.parent:
|
||||||
values_to_set["voucher_no"] = row.parent
|
values_to_set["voucher_no"] = row.parent
|
||||||
|
|
||||||
|
self.db_set("is_cancelled", 0)
|
||||||
|
|
||||||
if self.voucher_type != parent.doctype:
|
if self.voucher_type != parent.doctype:
|
||||||
values_to_set["voucher_type"] = parent.doctype
|
values_to_set["voucher_type"] = parent.doctype
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user