mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 16:34:46 +00:00
Merge pull request #45131 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
@@ -237,19 +237,22 @@ frappe.treeview_settings["Account"] = {
|
|||||||
},
|
},
|
||||||
post_render: function (treeview) {
|
post_render: function (treeview) {
|
||||||
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
|
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
|
||||||
treeview.page.set_primary_action(
|
if (treeview.can_create) {
|
||||||
__("New"),
|
treeview.page.set_primary_action(
|
||||||
function () {
|
__("New"),
|
||||||
let root_company = treeview.page.fields_dict.root_company.get_value();
|
function () {
|
||||||
|
let root_company = treeview.page.fields_dict.root_company.get_value();
|
||||||
if (root_company) {
|
if (root_company) {
|
||||||
frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
|
frappe.throw(__("Please add the account to root level Company - {0}"), [
|
||||||
} else {
|
root_company,
|
||||||
treeview.new_node();
|
]);
|
||||||
}
|
} else {
|
||||||
},
|
treeview.new_node();
|
||||||
"add"
|
}
|
||||||
);
|
},
|
||||||
|
"add"
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
toolbar: [
|
toolbar: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
args: {
|
args: {
|
||||||
bank_account: frm.doc.bank_account,
|
bank_account: frm.doc.bank_account,
|
||||||
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1),
|
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1),
|
||||||
|
company: frm.doc.company,
|
||||||
},
|
},
|
||||||
callback: (response) => {
|
callback: (response) => {
|
||||||
frm.set_value("account_opening_balance", response.message);
|
frm.set_value("account_opening_balance", response.message);
|
||||||
@@ -135,6 +136,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
args: {
|
args: {
|
||||||
bank_account: frm.doc.bank_account,
|
bank_account: frm.doc.bank_account,
|
||||||
till_date: frm.doc.bank_statement_to_date,
|
till_date: frm.doc.bank_statement_to_date,
|
||||||
|
company: frm.doc.company,
|
||||||
},
|
},
|
||||||
callback: (response) => {
|
callback: (response) => {
|
||||||
frm.cleared_balance = response.message;
|
frm.cleared_balance = response.message;
|
||||||
|
|||||||
@@ -79,10 +79,17 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_balance(bank_account, till_date):
|
def get_account_balance(bank_account, till_date, company):
|
||||||
# returns account balance till the specified date
|
# returns account balance till the specified date
|
||||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
||||||
filters = frappe._dict({"account": account, "report_date": till_date, "include_pos_transactions": 1})
|
filters = frappe._dict(
|
||||||
|
{
|
||||||
|
"account": account,
|
||||||
|
"report_date": till_date,
|
||||||
|
"include_pos_transactions": 1,
|
||||||
|
"company": company,
|
||||||
|
}
|
||||||
|
)
|
||||||
data = get_entries(filters)
|
data = get_entries(filters)
|
||||||
|
|
||||||
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
||||||
@@ -94,11 +101,7 @@ def get_account_balance(bank_account, till_date):
|
|||||||
|
|
||||||
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
||||||
|
|
||||||
bank_bal = (
|
return flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
|
||||||
flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
|
|
||||||
)
|
|
||||||
|
|
||||||
return bank_bal
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -490,13 +490,20 @@ def get_actual_expense(args):
|
|||||||
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
||||||
distribution = {}
|
distribution = {}
|
||||||
if monthly_distribution:
|
if monthly_distribution:
|
||||||
for d in frappe.db.sql(
|
mdp = frappe.qb.DocType("Monthly Distribution Percentage")
|
||||||
"""select mdp.month, mdp.percentage_allocation
|
md = frappe.qb.DocType("Monthly Distribution")
|
||||||
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
|
||||||
where mdp.parent=md.name and md.fiscal_year=%s""",
|
res = (
|
||||||
fiscal_year,
|
frappe.qb.from_(mdp)
|
||||||
as_dict=1,
|
.join(md)
|
||||||
):
|
.on(mdp.parent == md.name)
|
||||||
|
.select(mdp.month, mdp.percentage_allocation)
|
||||||
|
.where(md.fiscal_year == fiscal_year)
|
||||||
|
.where(md.name == monthly_distribution)
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for d in res:
|
||||||
distribution.setdefault(d.month, d.percentage_allocation)
|
distribution.setdefault(d.month, d.percentage_allocation)
|
||||||
|
|
||||||
dt = frappe.get_cached_value("Fiscal Year", fiscal_year, "year_start_date")
|
dt = frappe.get_cached_value("Fiscal Year", fiscal_year, "year_start_date")
|
||||||
|
|||||||
@@ -58,7 +58,9 @@
|
|||||||
"payment_account",
|
"payment_account",
|
||||||
"payment_channel",
|
"payment_channel",
|
||||||
"payment_order",
|
"payment_order",
|
||||||
"amended_from"
|
"amended_from",
|
||||||
|
"column_break_iiuv",
|
||||||
|
"phone_number"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -376,6 +378,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.payment_channel==\"Phone\"",
|
||||||
"fetch_from": "payment_gateway_account.payment_channel",
|
"fetch_from": "payment_gateway_account.payment_channel",
|
||||||
"fieldname": "payment_channel",
|
"fieldname": "payment_channel",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@@ -429,13 +432,22 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Party Name",
|
"label": "Party Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_iiuv",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "phone_number",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Phone Number"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-10-23 12:23:40.117336",
|
"modified": "2024-12-27 21:29:10.361894",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Request",
|
"name": "Payment Request",
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ class PaymentRequest(Document):
|
|||||||
sender=self.email_to,
|
sender=self.email_to,
|
||||||
currency=self.currency,
|
currency=self.currency,
|
||||||
payment_gateway=self.payment_gateway,
|
payment_gateway=self.payment_gateway,
|
||||||
|
phone_number=self.phone_number,
|
||||||
)
|
)
|
||||||
|
|
||||||
controller.validate_transaction_currency(self.currency)
|
controller.validate_transaction_currency(self.currency)
|
||||||
@@ -635,6 +636,7 @@ def make_payment_request(**args):
|
|||||||
"party": args.get("party") or ref_doc.get("customer"),
|
"party": args.get("party") or ref_doc.get("customer"),
|
||||||
"bank_account": bank_account,
|
"bank_account": bank_account,
|
||||||
"party_name": args.get("party_name") or ref_doc.get("customer_name"),
|
"party_name": args.get("party_name") or ref_doc.get("customer_name"),
|
||||||
|
"phone_number": args.get("phone_number") if args.get("phone_number") else None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"autoname": "format:ACC-PPR-{#####}",
|
"autoname": "format:ACC-PPR-{#####}",
|
||||||
"beta": 1,
|
|
||||||
"creation": "2023-03-30 21:28:39.793927",
|
"creation": "2023-03-30 21:28:39.793927",
|
||||||
"default_view": "List",
|
"default_view": "List",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -158,7 +157,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-08-27 14:48:56.715320",
|
"modified": "2025-01-08 08:22:14.798085",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Process Payment Reconciliation",
|
"name": "Process Payment Reconciliation",
|
||||||
@@ -192,4 +191,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"title_field": "company"
|
"title_field": "company"
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ def trigger_reconciliation_for_queued_docs():
|
|||||||
unique_filters = set()
|
unique_filters = set()
|
||||||
queue_size = 5
|
queue_size = 5
|
||||||
|
|
||||||
fields = ["company", "party_type", "party", "receivable_payable_account"]
|
fields = ["company", "party_type", "party", "receivable_payable_account", "default_advance_account"]
|
||||||
|
|
||||||
def get_filters_as_tuple(fields, doc):
|
def get_filters_as_tuple(fields, doc):
|
||||||
filters = ()
|
filters = ()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"autoname": "format:PPR-LOG-{##}",
|
"autoname": "format:PPR-LOG-{##}",
|
||||||
"beta": 1,
|
|
||||||
"creation": "2023-03-13 15:00:09.149681",
|
"creation": "2023-03-13 15:00:09.149681",
|
||||||
"default_view": "List",
|
"default_view": "List",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -110,7 +109,7 @@
|
|||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-11-02 11:32:12.254018",
|
"modified": "2025-01-08 08:22:19.104975",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Process Payment Reconciliation Log",
|
"name": "Process Payment Reconciliation Log",
|
||||||
|
|||||||
@@ -2494,6 +2494,34 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
self.assertEqual(len(actual), 3)
|
self.assertEqual(len(actual), 3)
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_invoice_against_returned_pr(self):
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||||
|
make_purchase_invoice as make_purchase_invoice_from_pr,
|
||||||
|
)
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||||
|
make_purchase_return_against_rejected_warehouse,
|
||||||
|
)
|
||||||
|
|
||||||
|
item = make_item("_Test Item For Invoice Against Returned PR", properties={"is_stock_item": 1}).name
|
||||||
|
|
||||||
|
original_value = frappe.db.get_single_value(
|
||||||
|
"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"
|
||||||
|
)
|
||||||
|
frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 0)
|
||||||
|
|
||||||
|
pr = make_purchase_receipt(item_code=item, qty=5, rejected_qty=5, rate=100)
|
||||||
|
pr_return = make_purchase_return_against_rejected_warehouse(pr.name)
|
||||||
|
pr_return.submit()
|
||||||
|
|
||||||
|
pi = make_purchase_invoice_from_pr(pr.name)
|
||||||
|
pi.save()
|
||||||
|
self.assertEqual(pi.items[0].qty, 5.0)
|
||||||
|
|
||||||
|
frappe.db.set_single_value(
|
||||||
|
"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", original_value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def set_advance_flag(company, flag, default_account):
|
def set_advance_flag(company, flag, default_account):
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
|
|||||||
@@ -994,47 +994,51 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.docstatus === 0 && !frm.doc.is_return) {
|
if (frm.doc.docstatus === 0 && !frm.doc.is_return) {
|
||||||
frm.add_custom_button(__("Fetch Timesheet"), function () {
|
frm.add_custom_button(
|
||||||
let d = new frappe.ui.Dialog({
|
__("Timesheet"),
|
||||||
title: __("Fetch Timesheet"),
|
function () {
|
||||||
fields: [
|
let d = new frappe.ui.Dialog({
|
||||||
{
|
title: __("Fetch Timesheet"),
|
||||||
label: __("From"),
|
fields: [
|
||||||
fieldname: "from_time",
|
{
|
||||||
fieldtype: "Date",
|
label: __("From"),
|
||||||
reqd: 1,
|
fieldname: "from_time",
|
||||||
|
fieldtype: "Date",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Column Break",
|
||||||
|
fieldname: "col_break_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("To"),
|
||||||
|
fieldname: "to_time",
|
||||||
|
fieldtype: "Date",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("Project"),
|
||||||
|
fieldname: "project",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Project",
|
||||||
|
default: frm.doc.project,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_action: function () {
|
||||||
|
const data = d.get_values();
|
||||||
|
frm.events.add_timesheet_data(frm, {
|
||||||
|
from_time: data.from_time,
|
||||||
|
to_time: data.to_time,
|
||||||
|
project: data.project,
|
||||||
|
});
|
||||||
|
d.hide();
|
||||||
},
|
},
|
||||||
{
|
primary_action_label: __("Get Timesheets"),
|
||||||
fieldtype: "Column Break",
|
});
|
||||||
fieldname: "col_break_1",
|
d.show();
|
||||||
},
|
},
|
||||||
{
|
__("Get Items From")
|
||||||
label: __("To"),
|
);
|
||||||
fieldname: "to_time",
|
|
||||||
fieldtype: "Date",
|
|
||||||
reqd: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: __("Project"),
|
|
||||||
fieldname: "project",
|
|
||||||
fieldtype: "Link",
|
|
||||||
options: "Project",
|
|
||||||
default: frm.doc.project,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
primary_action: function () {
|
|
||||||
const data = d.get_values();
|
|
||||||
frm.events.add_timesheet_data(frm, {
|
|
||||||
from_time: data.from_time,
|
|
||||||
to_time: data.to_time,
|
|
||||||
project: data.project,
|
|
||||||
});
|
|
||||||
d.hide();
|
|
||||||
},
|
|
||||||
primary_action_label: __("Get Timesheets"),
|
|
||||||
});
|
|
||||||
d.show();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.is_debit_note) {
|
if (frm.doc.is_debit_note) {
|
||||||
|
|||||||
@@ -142,7 +142,8 @@ def get_journal_entries(filters):
|
|||||||
where jvd.parent = jv.name and jv.docstatus=1
|
where jvd.parent = jv.name and jv.docstatus=1
|
||||||
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
|
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
|
||||||
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
|
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
|
||||||
and ifnull(jv.is_opening, 'No') = 'No'""",
|
and ifnull(jv.is_opening, 'No') = 'No'
|
||||||
|
and jv.company = %(company)s """,
|
||||||
filters,
|
filters,
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
@@ -163,6 +164,7 @@ def get_payment_entries(filters):
|
|||||||
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
||||||
and posting_date <= %(report_date)s
|
and posting_date <= %(report_date)s
|
||||||
and ifnull(clearance_date, '4000-01-01') > %(report_date)s
|
and ifnull(clearance_date, '4000-01-01') > %(report_date)s
|
||||||
|
and company = %(company)s
|
||||||
""",
|
""",
|
||||||
filters,
|
filters,
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
@@ -181,6 +183,7 @@ def get_pos_entries(filters):
|
|||||||
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
|
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
|
||||||
and account.name = sip.account and si.posting_date <= %(report_date)s and
|
and account.name = sip.account and si.posting_date <= %(report_date)s and
|
||||||
ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
|
ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
|
||||||
|
and si.company = %(company)s
|
||||||
order by
|
order by
|
||||||
si.posting_date ASC, si.name DESC
|
si.posting_date ASC, si.name DESC
|
||||||
""",
|
""",
|
||||||
|
|||||||
@@ -378,7 +378,7 @@
|
|||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt\nCapitalized\nDecapitalized",
|
"options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt\nCapitalized\nDecapitalized\nWork In Progress",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -595,7 +595,7 @@
|
|||||||
"link_fieldname": "target_asset"
|
"link_fieldname": "target_asset"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2024-08-26 23:28:29.095139",
|
"modified": "2024-12-26 14:23:20.968882",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset",
|
"name": "Asset",
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ class Asset(AccountsController):
|
|||||||
"Receipt",
|
"Receipt",
|
||||||
"Capitalized",
|
"Capitalized",
|
||||||
"Decapitalized",
|
"Decapitalized",
|
||||||
|
"Work In Progress",
|
||||||
]
|
]
|
||||||
supplier: DF.Link | None
|
supplier: DF.Link | None
|
||||||
total_asset_cost: DF.Currency
|
total_asset_cost: DF.Currency
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
frappe.listview_settings["Asset"] = {
|
frappe.listview_settings["Asset"] = {
|
||||||
add_fields: ["status"],
|
add_fields: ["status", "docstatus"],
|
||||||
|
has_indicator_for_draft: 1,
|
||||||
get_indicator: function (doc) {
|
get_indicator: function (doc) {
|
||||||
if (doc.status === "Fully Depreciated") {
|
if (doc.status === "Fully Depreciated") {
|
||||||
return [__("Fully Depreciated"), "green", "status,=,Fully Depreciated"];
|
return [__("Fully Depreciated"), "green", "status,=,Fully Depreciated"];
|
||||||
@@ -7,6 +8,8 @@ frappe.listview_settings["Asset"] = {
|
|||||||
return [__("Partially Depreciated"), "grey", "status,=,Partially Depreciated"];
|
return [__("Partially Depreciated"), "grey", "status,=,Partially Depreciated"];
|
||||||
} else if (doc.status === "Sold") {
|
} else if (doc.status === "Sold") {
|
||||||
return [__("Sold"), "green", "status,=,Sold"];
|
return [__("Sold"), "green", "status,=,Sold"];
|
||||||
|
} else if (doc.status === "Work In Progress") {
|
||||||
|
return [__("Work In Progress"), "orange", "status,=,Work In Progress"];
|
||||||
} else if (["Capitalized", "Decapitalized"].includes(doc.status)) {
|
} else if (["Capitalized", "Decapitalized"].includes(doc.status)) {
|
||||||
return [__(doc.status), "grey", "status,=," + doc.status];
|
return [__(doc.status), "grey", "status,=," + doc.status];
|
||||||
} else if (doc.status === "Scrapped") {
|
} else if (doc.status === "Scrapped") {
|
||||||
@@ -21,7 +24,7 @@ frappe.listview_settings["Asset"] = {
|
|||||||
return [__("Receipt"), "green", "status,=,Receipt"];
|
return [__("Receipt"), "green", "status,=,Receipt"];
|
||||||
} else if (doc.status === "Submitted") {
|
} else if (doc.status === "Submitted") {
|
||||||
return [__("Submitted"), "blue", "status,=,Submitted"];
|
return [__("Submitted"), "blue", "status,=,Submitted"];
|
||||||
} else if (doc.status === "Draft") {
|
} else if (doc.status === "Draft" || doc.docstatus === 0) {
|
||||||
return [__("Draft"), "red", "status,=,Draft"];
|
return [__("Draft"), "red", "status,=,Draft"];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1725,6 +1725,10 @@ def create_asset(**args):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if asset.is_composite_asset:
|
||||||
|
asset.gross_purchase_amount = 0
|
||||||
|
asset.purchase_amount = 0
|
||||||
|
|
||||||
if not args.do_not_save:
|
if not args.do_not_save:
|
||||||
try:
|
try:
|
||||||
asset.insert(ignore_if_duplicate=True)
|
asset.insert(ignore_if_duplicate=True)
|
||||||
|
|||||||
@@ -638,6 +638,7 @@ class AssetCapitalization(StockController):
|
|||||||
self.target_fixed_asset_account = get_asset_category_account(
|
self.target_fixed_asset_account = get_asset_category_account(
|
||||||
"fixed_asset_account", item=self.target_item_code, company=asset_doc.company
|
"fixed_asset_account", item=self.target_item_code, company=asset_doc.company
|
||||||
)
|
)
|
||||||
|
asset_doc.set_status("Work In Progress")
|
||||||
|
|
||||||
add_asset_activity(
|
add_asset_activity(
|
||||||
asset_doc.name,
|
asset_doc.name,
|
||||||
@@ -662,8 +663,9 @@ class AssetCapitalization(StockController):
|
|||||||
total_target_asset_value = flt(self.total_value, self.precision("total_value"))
|
total_target_asset_value = flt(self.total_value, self.precision("total_value"))
|
||||||
|
|
||||||
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
||||||
asset_doc.gross_purchase_amount = total_target_asset_value
|
asset_doc.gross_purchase_amount += total_target_asset_value
|
||||||
asset_doc.purchase_amount = total_target_asset_value
|
asset_doc.purchase_amount += total_target_asset_value
|
||||||
|
asset_doc.set_status("Work In Progress")
|
||||||
asset_doc.flags.ignore_validate = True
|
asset_doc.flags.ignore_validate = True
|
||||||
asset_doc.save()
|
asset_doc.save()
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class TestAssetCapitalization(unittest.TestCase):
|
|||||||
target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
|
target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
|
||||||
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
|
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
|
||||||
self.assertEqual(target_asset.purchase_amount, total_amount)
|
self.assertEqual(target_asset.purchase_amount, total_amount)
|
||||||
|
self.assertEqual(target_asset.status, "Work In Progress")
|
||||||
|
|
||||||
# Test Consumed Asset values
|
# Test Consumed Asset values
|
||||||
self.assertEqual(consumed_asset.db_get("status"), "Capitalized")
|
self.assertEqual(consumed_asset.db_get("status"), "Capitalized")
|
||||||
@@ -270,6 +271,7 @@ class TestAssetCapitalization(unittest.TestCase):
|
|||||||
target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
|
target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
|
||||||
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
|
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
|
||||||
self.assertEqual(target_asset.purchase_amount, total_amount)
|
self.assertEqual(target_asset.purchase_amount, total_amount)
|
||||||
|
self.assertEqual(target_asset.status, "Work In Progress")
|
||||||
|
|
||||||
# Test General Ledger Entries
|
# Test General Ledger Entries
|
||||||
expected_gle = {
|
expected_gle = {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from frappe.utils import (
|
|||||||
flt,
|
flt,
|
||||||
get_first_day,
|
get_first_day,
|
||||||
get_last_day,
|
get_last_day,
|
||||||
|
get_link_to_form,
|
||||||
getdate,
|
getdate,
|
||||||
is_last_day_of_the_month,
|
is_last_day_of_the_month,
|
||||||
month_diff,
|
month_diff,
|
||||||
@@ -1062,7 +1063,7 @@ def make_new_active_asset_depr_schedules_and_cancel_current_ones(
|
|||||||
if not current_asset_depr_schedule_doc:
|
if not current_asset_depr_schedule_doc:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format(
|
_("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format(
|
||||||
asset_doc.name, row.finance_book
|
get_link_to_form("Asset", asset_doc.name), row.finance_book
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1108,7 +1109,7 @@ def get_temp_asset_depr_schedule_doc(
|
|||||||
if not current_asset_depr_schedule_doc:
|
if not current_asset_depr_schedule_doc:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format(
|
_("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format(
|
||||||
asset_doc.name, row.finance_book
|
get_link_to_form("Asset", asset_doc.name), row.finance_book
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2353,6 +2353,7 @@ class AccountsController(TransactionBase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for d in self.get("payment_schedule"):
|
for d in self.get("payment_schedule"):
|
||||||
|
d.validate_from_to_dates("discount_date", "due_date")
|
||||||
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
|
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(
|
_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(
|
||||||
|
|||||||
@@ -1257,6 +1257,7 @@ def add_items_in_ste(ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_deta
|
|||||||
"item_code": row.item_details["rm_item_code"],
|
"item_code": row.item_details["rm_item_code"],
|
||||||
"subcontracted_item": row.item_details["main_item_code"],
|
"subcontracted_item": row.item_details["main_item_code"],
|
||||||
"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
|
"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
|
||||||
|
"use_serial_batch_fields": 1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1297,10 +1298,13 @@ def make_return_stock_entry_for_subcontract(
|
|||||||
if not value.qty:
|
if not value.qty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if item_details := value.get("item_details"):
|
||||||
|
item_details["serial_and_batch_bundle"] = None
|
||||||
|
|
||||||
if value.batch_no:
|
if value.batch_no:
|
||||||
for batch_no, qty in value.batch_no.items():
|
for batch_no, qty in value.batch_no.items():
|
||||||
if qty > 0:
|
if qty > 0:
|
||||||
add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field, batch_no)
|
add_items_in_ste(ste_doc, value, qty, rm_details, rm_detail_field, batch_no)
|
||||||
else:
|
else:
|
||||||
add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field)
|
add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field)
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,79 @@ class TestSubcontractingController(FrappeTestCase):
|
|||||||
|
|
||||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
||||||
|
|
||||||
|
def test_return_non_consumed_batch_materials(self):
|
||||||
|
"""
|
||||||
|
- Set backflush based on Material Transfer.
|
||||||
|
- Create SCO for item Subcontracted Item SA2.
|
||||||
|
- Transfer the batched components from Stores to Supplier warehouse with serial nos.
|
||||||
|
- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
|
||||||
|
- Create SCR for full qty against the SCO and change the qty of raw material.
|
||||||
|
- After that return the non consumed material back to the store from supplier's warehouse.
|
||||||
|
"""
|
||||||
|
|
||||||
|
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||||
|
set_backflush_based_on("Material Transferred for Subcontract")
|
||||||
|
service_item = make_item("Subcontracted Service FG Item A", properties={"is_stock_item": 0}).name
|
||||||
|
fg_item = make_item(
|
||||||
|
"Subcontracted FG Item SA2", properties={"is_stock_item": 1, "is_sub_contracted_item": 1}
|
||||||
|
).name
|
||||||
|
rm_item = make_item(
|
||||||
|
"Subcontracted Batch RM Item SA2",
|
||||||
|
properties={
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"batch_number_series": "BATCH-RM-IRM-.####",
|
||||||
|
},
|
||||||
|
).name
|
||||||
|
|
||||||
|
make_bom(item=fg_item, raw_materials=[rm_item], rate=100, currency="INR")
|
||||||
|
|
||||||
|
service_items = [
|
||||||
|
{
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"item_code": service_item,
|
||||||
|
"qty": 5,
|
||||||
|
"rate": 100,
|
||||||
|
"fg_item": fg_item,
|
||||||
|
"fg_item_qty": 5,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
sco = get_subcontracting_order(service_items=service_items)
|
||||||
|
rm_items = get_rm_items(sco.supplied_items)
|
||||||
|
rm_items[0]["qty"] += 1
|
||||||
|
itemwise_details = make_stock_in_entry(rm_items=rm_items)
|
||||||
|
|
||||||
|
for item in rm_items:
|
||||||
|
item["sco_rm_detail"] = sco.items[0].name
|
||||||
|
|
||||||
|
make_stock_transfer_entry(
|
||||||
|
sco_no=sco.name,
|
||||||
|
rm_items=rm_items,
|
||||||
|
itemwise_details=copy.deepcopy(itemwise_details),
|
||||||
|
)
|
||||||
|
|
||||||
|
scr1 = make_subcontracting_receipt(sco.name)
|
||||||
|
scr1.save()
|
||||||
|
scr1.supplied_items[0].consumed_qty = 5
|
||||||
|
scr1.submit()
|
||||||
|
|
||||||
|
for key, value in get_supplied_items(scr1).items():
|
||||||
|
transferred_detais = itemwise_details.get(key)
|
||||||
|
self.assertEqual(value.qty, 5)
|
||||||
|
self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
|
||||||
|
|
||||||
|
sco.load_from_db()
|
||||||
|
self.assertEqual(sco.supplied_items[0].consumed_qty, 5)
|
||||||
|
doc = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items])
|
||||||
|
doc.save()
|
||||||
|
self.assertEqual(doc.items[0].qty, 1)
|
||||||
|
self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
|
||||||
|
self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
|
||||||
|
self.assertTrue(doc.items[0].batch_no)
|
||||||
|
self.assertTrue(doc.items[0].use_serial_batch_fields)
|
||||||
|
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
||||||
|
|
||||||
def test_return_non_consumed_materials(self):
|
def test_return_non_consumed_materials(self):
|
||||||
"""
|
"""
|
||||||
- Set backflush based on Material Transfer.
|
- Set backflush based on Material Transfer.
|
||||||
|
|||||||
@@ -130,6 +130,32 @@ frappe.ui.form.on("Work Order", {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
allow_alternative_item: function (frm) {
|
||||||
|
let has_alternative = false;
|
||||||
|
if (frm.doc.required_items) {
|
||||||
|
has_alternative = frm.doc.required_items.find((i) => i.allow_alternative_item === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm.doc.allow_alternative_item && frm.doc.docstatus === 0 && has_alternative) {
|
||||||
|
frm.add_custom_button(__("Alternate Item"), () => {
|
||||||
|
erpnext.utils.select_alternate_items({
|
||||||
|
frm: frm,
|
||||||
|
child_docname: "required_items",
|
||||||
|
warehouse_field: "source_warehouse",
|
||||||
|
child_doctype: "Work Order Item",
|
||||||
|
original_item_field: "original_item",
|
||||||
|
condition: (d) => {
|
||||||
|
if (d.allow_alternative_item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.remove_custom_button(__("Alternate Item"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
erpnext.work_order.set_custom_buttons(frm);
|
erpnext.work_order.set_custom_buttons(frm);
|
||||||
@@ -163,26 +189,6 @@ frappe.ui.form.on("Work Order", {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.required_items && frm.doc.allow_alternative_item) {
|
|
||||||
const has_alternative = frm.doc.required_items.find((i) => i.allow_alternative_item === 1);
|
|
||||||
if (frm.doc.docstatus == 0 && has_alternative) {
|
|
||||||
frm.add_custom_button(__("Alternate Item"), () => {
|
|
||||||
erpnext.utils.select_alternate_items({
|
|
||||||
frm: frm,
|
|
||||||
child_docname: "required_items",
|
|
||||||
warehouse_field: "source_warehouse",
|
|
||||||
child_doctype: "Work Order Item",
|
|
||||||
original_item_field: "original_item",
|
|
||||||
condition: (d) => {
|
|
||||||
if (d.allow_alternative_item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frm.doc.status == "Completed") {
|
if (frm.doc.status == "Completed") {
|
||||||
if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") {
|
if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
@@ -210,6 +216,7 @@ frappe.ui.form.on("Work Order", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frm.trigger("add_custom_button_to_return_components");
|
frm.trigger("add_custom_button_to_return_components");
|
||||||
|
frm.trigger("allow_alternative_item");
|
||||||
},
|
},
|
||||||
|
|
||||||
add_custom_button_to_return_components: function (frm) {
|
add_custom_button_to_return_components: function (frm) {
|
||||||
@@ -540,6 +547,9 @@ frappe.ui.form.on("Work Order", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Work Order Item", {
|
frappe.ui.form.on("Work Order Item", {
|
||||||
|
allow_alternative_item(frm) {
|
||||||
|
frm.trigger("allow_alternative_item");
|
||||||
|
},
|
||||||
source_warehouse: function (frm, cdt, cdn) {
|
source_warehouse: function (frm, cdt, cdn) {
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
if (!row.item_code) {
|
if (!row.item_code) {
|
||||||
@@ -618,7 +628,7 @@ erpnext.work_order = {
|
|||||||
set_custom_buttons: function (frm) {
|
set_custom_buttons: function (frm) {
|
||||||
var doc = frm.doc;
|
var doc = frm.doc;
|
||||||
|
|
||||||
if (doc.status !== "Closed") {
|
if (doc.docstatus === 1 && doc.status !== "Closed") {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
__("Close"),
|
__("Close"),
|
||||||
function () {
|
function () {
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ def get_returned_materials(work_orders):
|
|||||||
|
|
||||||
raw_materials = frappe.get_all(
|
raw_materials = frappe.get_all(
|
||||||
"Stock Entry",
|
"Stock Entry",
|
||||||
fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"],
|
fields=[
|
||||||
|
"`tabStock Entry`.`work_order`",
|
||||||
|
"`tabStock Entry Detail`.`item_code`",
|
||||||
|
"`tabStock Entry Detail`.`qty`",
|
||||||
|
],
|
||||||
filters=[
|
filters=[
|
||||||
["Stock Entry", "is_return", "=", 1],
|
["Stock Entry", "is_return", "=", 1],
|
||||||
["Stock Entry Detail", "docstatus", "=", 1],
|
["Stock Entry Detail", "docstatus", "=", 1],
|
||||||
@@ -59,12 +63,14 @@ def get_returned_materials(work_orders):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for d in raw_materials:
|
for d in raw_materials:
|
||||||
raw_materials_qty[d.item_code] += d.qty
|
key = (d.work_order, d.item_code)
|
||||||
|
raw_materials_qty[key] += d.qty
|
||||||
|
|
||||||
for row in work_orders:
|
for row in work_orders:
|
||||||
row.returned_qty = 0.0
|
row.returned_qty = 0.0
|
||||||
if raw_materials_qty.get(row.raw_material_item_code):
|
key = (row.parent, row.raw_material_item_code)
|
||||||
row.returned_qty = raw_materials_qty.get(row.raw_material_item_code)
|
if raw_materials_qty.get(key):
|
||||||
|
row.returned_qty = raw_materials_qty.get(key)
|
||||||
|
|
||||||
|
|
||||||
def get_fields():
|
def get_fields():
|
||||||
|
|||||||
@@ -386,3 +386,4 @@ 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
|
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts
|
||||||
|
erpnext.patches.v15_0.update_asset_status_to_work_in_progress
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
Asset = frappe.qb.DocType("Asset")
|
||||||
|
query = (
|
||||||
|
frappe.qb.update(Asset)
|
||||||
|
.set(Asset.status, "Work In Progress")
|
||||||
|
.where((Asset.docstatus == 0) & (Asset.is_composite_asset == 1))
|
||||||
|
)
|
||||||
|
query.run()
|
||||||
@@ -147,29 +147,32 @@ frappe.ui.form.on("Project", {
|
|||||||
set_project_status_button: function (frm) {
|
set_project_status_button: function (frm) {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
__("Set Project Status"),
|
__("Set Project Status"),
|
||||||
() => {
|
() => frm.events.get_project_status_dialog(frm).show(),
|
||||||
let d = new frappe.ui.Dialog({
|
|
||||||
title: __("Set Project Status"),
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
fieldname: "status",
|
|
||||||
fieldtype: "Select",
|
|
||||||
label: "Status",
|
|
||||||
reqd: 1,
|
|
||||||
options: "Completed\nCancelled",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
primary_action: function () {
|
|
||||||
frm.events.set_status(frm, d.get_values().status);
|
|
||||||
d.hide();
|
|
||||||
},
|
|
||||||
primary_action_label: __("Set Project Status"),
|
|
||||||
}).show();
|
|
||||||
},
|
|
||||||
__("Actions")
|
__("Actions")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get_project_status_dialog: function (frm) {
|
||||||
|
const dialog = new frappe.ui.Dialog({
|
||||||
|
title: __("Set Project Status"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldname: "status",
|
||||||
|
fieldtype: "Select",
|
||||||
|
label: "Status",
|
||||||
|
reqd: 1,
|
||||||
|
options: "Completed\nCancelled",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_action: function () {
|
||||||
|
frm.events.set_status(frm, dialog.get_values().status);
|
||||||
|
dialog.hide();
|
||||||
|
},
|
||||||
|
primary_action_label: __("Set Project Status"),
|
||||||
|
});
|
||||||
|
return dialog;
|
||||||
|
},
|
||||||
|
|
||||||
create_duplicate: function (frm) {
|
create_duplicate: function (frm) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
frappe.prompt("Project Name", (data) => {
|
frappe.prompt("Project Name", (data) => {
|
||||||
@@ -188,7 +191,7 @@ frappe.ui.form.on("Project", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_status: function (frm, status) {
|
set_status: function (frm, status) {
|
||||||
frappe.confirm(__("Set Project and all Tasks to status {0}?", [status.bold()]), () => {
|
frappe.confirm(__("Set Project and all Tasks to status {0}?", [__(status).bold()]), () => {
|
||||||
frappe
|
frappe
|
||||||
.xcall("erpnext.projects.doctype.project.project.set_project_status", {
|
.xcall("erpnext.projects.doctype.project.project.set_project_status", {
|
||||||
project: frm.doc.name,
|
project: frm.doc.name,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make_dt() {
|
make_dt() {
|
||||||
var me = this;
|
const me = this;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
|
||||||
args: {
|
args: {
|
||||||
@@ -193,6 +193,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
|
|||||||
args: {
|
args: {
|
||||||
bank_account: this.bank_account,
|
bank_account: this.bank_account,
|
||||||
till_date: this.bank_statement_to_date,
|
till_date: this.bank_statement_to_date,
|
||||||
|
company: this.company,
|
||||||
},
|
},
|
||||||
callback: (response) => (this.cleared_balance = response.message),
|
callback: (response) => (this.cleared_balance = response.message),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -303,11 +303,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
const me = this;
|
const me = this;
|
||||||
if (!this.frm.is_new() && this.frm.doc.docstatus === 0 && frappe.model.can_create("Quality Inspection")) {
|
if (!this.frm.is_new() && this.frm.doc.docstatus === 0 && frappe.model.can_create("Quality Inspection") && this.frm.doc.update_stock) {
|
||||||
this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
|
this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
|
||||||
me.make_quality_inspection();
|
me.make_quality_inspection();
|
||||||
}, __("Create"));
|
}, __("Create"));
|
||||||
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inspection_type = ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"].includes(this.frm.doc.doctype)
|
const inspection_type = ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"].includes(this.frm.doc.doctype)
|
||||||
@@ -974,7 +973,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction_date() {
|
transaction_date() {
|
||||||
this.apply_pricing_rule()
|
|
||||||
if (this.frm.doc.transaction_date) {
|
if (this.frm.doc.transaction_date) {
|
||||||
this.frm.transaction_date = this.frm.doc.transaction_date;
|
this.frm.transaction_date = this.frm.doc.transaction_date;
|
||||||
frappe.ui.form.trigger(this.frm.doc.doctype, "currency");
|
frappe.ui.form.trigger(this.frm.doc.doctype, "currency");
|
||||||
@@ -983,7 +981,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
|
|
||||||
posting_date() {
|
posting_date() {
|
||||||
var me = this;
|
var me = this;
|
||||||
me.apply_pricing_rule()
|
|
||||||
if (this.frm.doc.posting_date) {
|
if (this.frm.doc.posting_date) {
|
||||||
this.frm.posting_date = this.frm.doc.posting_date;
|
this.frm.posting_date = this.frm.doc.posting_date;
|
||||||
|
|
||||||
|
|||||||
@@ -58,13 +58,17 @@ def search_by_term(search_term, warehouse, price_list):
|
|||||||
item_stock_qty = item_stock_qty // item.get("conversion_factor", 1)
|
item_stock_qty = item_stock_qty // item.get("conversion_factor", 1)
|
||||||
item.update({"actual_qty": item_stock_qty})
|
item.update({"actual_qty": item_stock_qty})
|
||||||
|
|
||||||
|
price_filters = {
|
||||||
|
"price_list": price_list,
|
||||||
|
"item_code": item_code,
|
||||||
|
}
|
||||||
|
|
||||||
|
if batch_no:
|
||||||
|
price_filters["batch_no"] = batch_no
|
||||||
|
|
||||||
price = frappe.get_list(
|
price = frappe.get_list(
|
||||||
doctype="Item Price",
|
doctype="Item Price",
|
||||||
filters={
|
filters=price_filters,
|
||||||
"price_list": price_list,
|
|
||||||
"item_code": item_code,
|
|
||||||
"batch_no": batch_no,
|
|
||||||
},
|
|
||||||
fields=["uom", "currency", "price_list_rate", "batch_no"],
|
fields=["uom", "currency", "price_list_rate", "batch_no"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -928,10 +928,13 @@ erpnext.PointOfSale.ItemCart = class {
|
|||||||
const me = this;
|
const me = this;
|
||||||
dfs.forEach((df) => {
|
dfs.forEach((df) => {
|
||||||
this[`customer_${df.fieldname}_field`] = frappe.ui.form.make_control({
|
this[`customer_${df.fieldname}_field`] = frappe.ui.form.make_control({
|
||||||
df: { ...df, onchange: handle_customer_field_change },
|
df: df,
|
||||||
parent: $customer_form.find(`.${df.fieldname}-field`),
|
parent: $customer_form.find(`.${df.fieldname}-field`),
|
||||||
render_input: true,
|
render_input: true,
|
||||||
});
|
});
|
||||||
|
this[`customer_${df.fieldname}_field`].$input?.on("blur", () => {
|
||||||
|
handle_customer_field_change.apply(this[`customer_${df.fieldname}_field`]);
|
||||||
|
});
|
||||||
this[`customer_${df.fieldname}_field`].set_value(this.customer_info[df.fieldname]);
|
this[`customer_${df.fieldname}_field`].set_value(this.customer_info[df.fieldname]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -210,10 +210,21 @@ erpnext.PointOfSale.ItemDetails = class {
|
|||||||
|
|
||||||
make_auto_serial_selection_btn(item) {
|
make_auto_serial_selection_btn(item) {
|
||||||
if (item.has_serial_no || item.has_batch_no) {
|
if (item.has_serial_no || item.has_batch_no) {
|
||||||
const label = item.has_serial_no ? __("Select Serial No") : __("Select Batch No");
|
if (item.has_serial_no && item.has_batch_no) {
|
||||||
this.$form_container.append(
|
this.$form_container.append(
|
||||||
`<div class="btn btn-sm btn-secondary auto-fetch-btn">${label}</div>`
|
`<div class="btn btn-sm btn-secondary auto-fetch-btn" style="margin-top: 6px">${__(
|
||||||
);
|
"Select Serial No / Batch No"
|
||||||
|
)}</div>`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const classname = item.has_serial_no ? ".serial_no-control" : ".batch_no-control";
|
||||||
|
const label = item.has_serial_no ? __("Select Serial No") : __("Select Batch No");
|
||||||
|
this.$form_container
|
||||||
|
.find(classname)
|
||||||
|
.append(
|
||||||
|
`<div class="btn btn-sm btn-secondary auto-fetch-btn" style="margin-top: 6px">${label}</div>`
|
||||||
|
);
|
||||||
|
}
|
||||||
this.$form_container.find(".serial_no-control").find("textarea").css("height", "6rem");
|
this.$form_container.find(".serial_no-control").find("textarea").css("height", "6rem");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -328,13 +328,16 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filter_items({ search_term = "" } = {}) {
|
filter_items({ search_term = "" } = {}) {
|
||||||
|
const selling_price_list = this.events.get_frm().doc.selling_price_list;
|
||||||
|
|
||||||
if (search_term) {
|
if (search_term) {
|
||||||
search_term = search_term.toLowerCase();
|
search_term = search_term.toLowerCase();
|
||||||
|
|
||||||
// memoize
|
// memoize
|
||||||
this.search_index = this.search_index || {};
|
this.search_index = this.search_index || {};
|
||||||
if (this.search_index[search_term]) {
|
this.search_index[selling_price_list] = this.search_index[selling_price_list] || {};
|
||||||
const items = this.search_index[search_term];
|
if (this.search_index[selling_price_list][search_term]) {
|
||||||
|
const items = this.search_index[selling_price_list][search_term];
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.render_item_list(items);
|
this.render_item_list(items);
|
||||||
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||||
@@ -346,7 +349,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { items, serial_no, batch_no, barcode } = message;
|
const { items, serial_no, batch_no, barcode } = message;
|
||||||
if (search_term && !barcode) {
|
if (search_term && !barcode) {
|
||||||
this.search_index[search_term] = items;
|
this.search_index[selling_price_list][search_term] = items;
|
||||||
}
|
}
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.render_item_list(items);
|
this.render_item_list(items);
|
||||||
|
|||||||
@@ -1183,6 +1183,9 @@ def make_purchase_invoice(source_name, target_doc=None, args=None):
|
|||||||
return pending_qty, 0
|
return pending_qty, 0
|
||||||
|
|
||||||
returned_qty = flt(returned_qty_map.get(item_row.name, 0))
|
returned_qty = flt(returned_qty_map.get(item_row.name, 0))
|
||||||
|
if item_row.rejected_qty and returned_qty:
|
||||||
|
returned_qty -= item_row.rejected_qty
|
||||||
|
|
||||||
if returned_qty:
|
if returned_qty:
|
||||||
if returned_qty >= pending_qty:
|
if returned_qty >= pending_qty:
|
||||||
pending_qty = 0
|
pending_qty = 0
|
||||||
|
|||||||
@@ -1557,7 +1557,7 @@ def get_type_of_transaction(parent_doc, child_row):
|
|||||||
elif parent_doc.get("doctype") == "Stock Reconciliation":
|
elif parent_doc.get("doctype") == "Stock Reconciliation":
|
||||||
type_of_transaction = "Inward"
|
type_of_transaction = "Inward"
|
||||||
|
|
||||||
if parent_doc.get("is_return"):
|
if parent_doc.get("is_return") and parent_doc.get("doctype") != "Stock Entry":
|
||||||
type_of_transaction = "Inward"
|
type_of_transaction = "Inward"
|
||||||
if (
|
if (
|
||||||
parent_doc.get("doctype") in ["Purchase Receipt", "Purchase Invoice"]
|
parent_doc.get("doctype") in ["Purchase Receipt", "Purchase Invoice"]
|
||||||
|
|||||||
@@ -1093,6 +1093,7 @@ class StockReconciliation(StockController):
|
|||||||
posting_date=doc.posting_date,
|
posting_date=doc.posting_date,
|
||||||
posting_time=doc.posting_time,
|
posting_time=doc.posting_time,
|
||||||
ignore_voucher_nos=[doc.voucher_no],
|
ignore_voucher_nos=[doc.voucher_no],
|
||||||
|
for_stock_levels=True,
|
||||||
)
|
)
|
||||||
or 0
|
or 0
|
||||||
) * -1
|
) * -1
|
||||||
|
|||||||
@@ -317,7 +317,6 @@ class StockBalanceReport:
|
|||||||
.where((sle.docstatus < 2) & (sle.is_cancelled == 0))
|
.where((sle.docstatus < 2) & (sle.is_cancelled == 0))
|
||||||
.orderby(sle.posting_datetime)
|
.orderby(sle.posting_datetime)
|
||||||
.orderby(sle.creation)
|
.orderby(sle.creation)
|
||||||
.orderby(sle.actual_qty)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
query = self.apply_inventory_dimensions_filters(query, sle)
|
query = self.apply_inventory_dimensions_filters(query, sle)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "Open\nReplied\nHold\nClosed",
|
"options": "Open\nReplied\nOn Hold\nClosed",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"show_in_filter": 1
|
"show_in_filter": 1
|
||||||
|
|||||||
Reference in New Issue
Block a user