mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-14 02:31:21 +00:00
Merge pull request #41151 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -194,12 +194,18 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
def validate_budget_records(args, budget_records, expense_amount):
|
def validate_budget_records(args, budget_records, expense_amount):
|
||||||
for budget in budget_records:
|
for budget in budget_records:
|
||||||
if flt(budget.budget_amount):
|
if flt(budget.budget_amount):
|
||||||
amount = expense_amount or get_amount(args, budget)
|
|
||||||
yearly_action, monthly_action = get_actions(args, budget)
|
yearly_action, monthly_action = get_actions(args, budget)
|
||||||
|
args["for_material_request"] = budget.for_material_request
|
||||||
|
args["for_purchase_order"] = budget.for_purchase_order
|
||||||
|
|
||||||
if yearly_action in ("Stop", "Warn"):
|
if yearly_action in ("Stop", "Warn"):
|
||||||
compare_expense_with_budget(
|
compare_expense_with_budget(
|
||||||
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
|
args,
|
||||||
|
flt(budget.budget_amount),
|
||||||
|
_("Annual"),
|
||||||
|
yearly_action,
|
||||||
|
budget.budget_against,
|
||||||
|
expense_amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
if monthly_action in ["Stop", "Warn"]:
|
||||||
@@ -215,18 +221,27 @@ def validate_budget_records(args, budget_records, expense_amount):
|
|||||||
_("Accumulated Monthly"),
|
_("Accumulated Monthly"),
|
||||||
monthly_action,
|
monthly_action,
|
||||||
budget.budget_against,
|
budget.budget_against,
|
||||||
amount,
|
expense_amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
||||||
actual_expense = get_actual_expense(args)
|
args.actual_expense, args.requested_amount, args.ordered_amount = get_actual_expense(args), 0, 0
|
||||||
total_expense = actual_expense + amount
|
if not amount:
|
||||||
|
args.requested_amount, args.ordered_amount = get_requested_amount(args), get_ordered_amount(args)
|
||||||
|
|
||||||
|
if args.get("doctype") == "Material Request" and args.for_material_request:
|
||||||
|
amount = args.requested_amount + args.ordered_amount
|
||||||
|
|
||||||
|
elif args.get("doctype") == "Purchase Order" and args.for_purchase_order:
|
||||||
|
amount = args.ordered_amount
|
||||||
|
|
||||||
|
total_expense = args.actual_expense + amount
|
||||||
|
|
||||||
if total_expense > budget_amount:
|
if total_expense > budget_amount:
|
||||||
if actual_expense > budget_amount:
|
if args.actual_expense > budget_amount:
|
||||||
error_tense = _("is already")
|
error_tense = _("is already")
|
||||||
diff = actual_expense - budget_amount
|
diff = args.actual_expense - budget_amount
|
||||||
else:
|
else:
|
||||||
error_tense = _("will be")
|
error_tense = _("will be")
|
||||||
diff = total_expense - budget_amount
|
diff = total_expense - budget_amount
|
||||||
@@ -243,6 +258,8 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
frappe.bold(fmt_money(diff, currency=currency)),
|
frappe.bold(fmt_money(diff, currency=currency)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
msg += get_expense_breakup(args, currency, budget_against)
|
||||||
|
|
||||||
if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
|
if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
|
||||||
frappe.session.user
|
frappe.session.user
|
||||||
):
|
):
|
||||||
@@ -254,6 +271,83 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_expense_breakup(args, currency, budget_against):
|
||||||
|
msg = "<hr>Total Expenses booked through - <ul>"
|
||||||
|
|
||||||
|
common_filters = frappe._dict(
|
||||||
|
{
|
||||||
|
args.budget_against_field: budget_against,
|
||||||
|
"account": args.account,
|
||||||
|
"company": args.company,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
msg += (
|
||||||
|
"<li>"
|
||||||
|
+ frappe.utils.get_link_to_report(
|
||||||
|
"General Ledger",
|
||||||
|
label="Actual Expenses",
|
||||||
|
filters=common_filters.copy().update(
|
||||||
|
{
|
||||||
|
"from_date": frappe.get_cached_value("Fiscal Year", args.fiscal_year, "year_start_date"),
|
||||||
|
"to_date": frappe.get_cached_value("Fiscal Year", args.fiscal_year, "year_end_date"),
|
||||||
|
"is_cancelled": 0,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ " - "
|
||||||
|
+ frappe.bold(fmt_money(args.actual_expense, currency=currency))
|
||||||
|
+ "</li>"
|
||||||
|
)
|
||||||
|
|
||||||
|
msg += (
|
||||||
|
"<li>"
|
||||||
|
+ frappe.utils.get_link_to_report(
|
||||||
|
"Material Request",
|
||||||
|
label="Material Requests",
|
||||||
|
report_type="Report Builder",
|
||||||
|
doctype="Material Request",
|
||||||
|
filters=common_filters.copy().update(
|
||||||
|
{
|
||||||
|
"status": [["!=", "Stopped"]],
|
||||||
|
"docstatus": 1,
|
||||||
|
"material_request_type": "Purchase",
|
||||||
|
"schedule_date": [["fiscal year", "2023-2024"]],
|
||||||
|
"item_code": args.item_code,
|
||||||
|
"per_ordered": [["<", 100]],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ " - "
|
||||||
|
+ frappe.bold(fmt_money(args.requested_amount, currency=currency))
|
||||||
|
+ "</li>"
|
||||||
|
)
|
||||||
|
|
||||||
|
msg += (
|
||||||
|
"<li>"
|
||||||
|
+ frappe.utils.get_link_to_report(
|
||||||
|
"Purchase Order",
|
||||||
|
label="Unbilled Orders",
|
||||||
|
report_type="Report Builder",
|
||||||
|
doctype="Purchase Order",
|
||||||
|
filters=common_filters.copy().update(
|
||||||
|
{
|
||||||
|
"status": [["!=", "Closed"]],
|
||||||
|
"docstatus": 1,
|
||||||
|
"transaction_date": [["fiscal year", "2023-2024"]],
|
||||||
|
"item_code": args.item_code,
|
||||||
|
"per_billed": [["<", 100]],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ " - "
|
||||||
|
+ frappe.bold(fmt_money(args.ordered_amount, currency=currency))
|
||||||
|
+ "</li></ul>"
|
||||||
|
)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def get_actions(args, budget):
|
def get_actions(args, budget):
|
||||||
yearly_action = budget.action_if_annual_budget_exceeded
|
yearly_action = budget.action_if_annual_budget_exceeded
|
||||||
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
||||||
@@ -269,23 +363,9 @@ def get_actions(args, budget):
|
|||||||
return yearly_action, monthly_action
|
return yearly_action, monthly_action
|
||||||
|
|
||||||
|
|
||||||
def get_amount(args, budget):
|
def get_requested_amount(args):
|
||||||
amount = 0
|
|
||||||
|
|
||||||
if args.get("doctype") == "Material Request" and budget.for_material_request:
|
|
||||||
amount = (
|
|
||||||
get_requested_amount(args, budget) + get_ordered_amount(args, budget) + get_actual_expense(args)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
|
|
||||||
amount = get_ordered_amount(args, budget) + get_actual_expense(args)
|
|
||||||
|
|
||||||
return amount
|
|
||||||
|
|
||||||
|
|
||||||
def get_requested_amount(args, budget):
|
|
||||||
item_code = args.get("item_code")
|
item_code = args.get("item_code")
|
||||||
condition = get_other_condition(args, budget, "Material Request")
|
condition = get_other_condition(args, "Material Request")
|
||||||
|
|
||||||
data = frappe.db.sql(
|
data = frappe.db.sql(
|
||||||
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
||||||
@@ -299,9 +379,9 @@ def get_requested_amount(args, budget):
|
|||||||
return data[0][0] if data else 0
|
return data[0][0] if data else 0
|
||||||
|
|
||||||
|
|
||||||
def get_ordered_amount(args, budget):
|
def get_ordered_amount(args):
|
||||||
item_code = args.get("item_code")
|
item_code = args.get("item_code")
|
||||||
condition = get_other_condition(args, budget, "Purchase Order")
|
condition = get_other_condition(args, "Purchase Order")
|
||||||
|
|
||||||
data = frappe.db.sql(
|
data = frappe.db.sql(
|
||||||
f""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
f""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||||
@@ -315,7 +395,7 @@ def get_ordered_amount(args, budget):
|
|||||||
return data[0][0] if data else 0
|
return data[0][0] if data else 0
|
||||||
|
|
||||||
|
|
||||||
def get_other_condition(args, budget, for_doc):
|
def get_other_condition(args, for_doc):
|
||||||
condition = "expense_account = '%s'" % (args.expense_account)
|
condition = "expense_account = '%s'" % (args.expense_account)
|
||||||
budget_against_field = args.get("budget_against_field")
|
budget_against_field = args.get("budget_against_field")
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-01-31 13:22:58.916273",
|
"modified": "2024-04-24 10:55:54.083042",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cost Center",
|
"name": "Cost Center",
|
||||||
@@ -163,6 +163,15 @@
|
|||||||
{
|
{
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Purchase User"
|
"role": "Purchase User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Employee",
|
||||||
|
"select": 1,
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "parent_cost_center, is_group",
|
"search_fields": "parent_cost_center, is_group",
|
||||||
|
|||||||
@@ -118,9 +118,17 @@
|
|||||||
{
|
{
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Employee"
|
"role": "Employee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"read": 1,
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"read": 1,
|
||||||
|
"role": "Stock Manager"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "name",
|
"sort_field": "name",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ frappe.ui.form.on("Payment Order", {
|
|||||||
target: frm,
|
target: frm,
|
||||||
date_field: "posting_date",
|
date_field: "posting_date",
|
||||||
setters: {
|
setters: {
|
||||||
|
party_type: "Supplier",
|
||||||
party: frm.doc.supplier || "",
|
party: frm.doc.supplier || "",
|
||||||
},
|
},
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
@@ -91,6 +92,7 @@ frappe.ui.form.on("Payment Order", {
|
|||||||
source_doctype: "Payment Request",
|
source_doctype: "Payment Request",
|
||||||
target: frm,
|
target: frm,
|
||||||
setters: {
|
setters: {
|
||||||
|
party_type: "Supplier",
|
||||||
party: frm.doc.supplier || "",
|
party: frm.doc.supplier || "",
|
||||||
},
|
},
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class PaymentRequest(Document):
|
|||||||
self.status = "Draft"
|
self.status = "Draft"
|
||||||
self.validate_reference_document()
|
self.validate_reference_document()
|
||||||
self.validate_payment_request_amount()
|
self.validate_payment_request_amount()
|
||||||
self.validate_currency()
|
# self.validate_currency()
|
||||||
self.validate_subscription_details()
|
self.validate_subscription_details()
|
||||||
|
|
||||||
def validate_reference_document(self):
|
def validate_reference_document(self):
|
||||||
@@ -276,21 +276,17 @@ class PaymentRequest(Document):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
|
||||||
|
amount = payment_entry.base_paid_amount
|
||||||
|
else:
|
||||||
|
amount = self.grand_total
|
||||||
|
|
||||||
|
payment_entry.received_amount = amount
|
||||||
|
payment_entry.get("references")[0].allocated_amount = amount
|
||||||
|
|
||||||
for dimension in get_accounting_dimensions():
|
for dimension in get_accounting_dimensions():
|
||||||
payment_entry.update({dimension: self.get(dimension)})
|
payment_entry.update({dimension: self.get(dimension)})
|
||||||
|
|
||||||
if payment_entry.difference_amount:
|
|
||||||
company_details = get_company_defaults(ref_doc.company)
|
|
||||||
|
|
||||||
payment_entry.append(
|
|
||||||
"deductions",
|
|
||||||
{
|
|
||||||
"account": company_details.exchange_gain_loss_account,
|
|
||||||
"cost_center": company_details.cost_center,
|
|
||||||
"amount": payment_entry.difference_amount,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if submit:
|
if submit:
|
||||||
payment_entry.insert(ignore_permissions=True)
|
payment_entry.insert(ignore_permissions=True)
|
||||||
payment_entry.submit()
|
payment_entry.submit()
|
||||||
@@ -432,6 +428,12 @@ def make_payment_request(**args):
|
|||||||
pr = frappe.get_doc("Payment Request", draft_payment_request)
|
pr = frappe.get_doc("Payment Request", draft_payment_request)
|
||||||
else:
|
else:
|
||||||
pr = frappe.new_doc("Payment Request")
|
pr = frappe.new_doc("Payment Request")
|
||||||
|
|
||||||
|
if not args.get("payment_request_type"):
|
||||||
|
args["payment_request_type"] = (
|
||||||
|
"Outward" if args.get("dt") in ["Purchase Order", "Purchase Invoice"] else "Inward"
|
||||||
|
)
|
||||||
|
|
||||||
pr.update(
|
pr.update(
|
||||||
{
|
{
|
||||||
"payment_gateway_account": gateway_account.get("name"),
|
"payment_gateway_account": gateway_account.get("name"),
|
||||||
@@ -490,9 +492,9 @@ def get_amount(ref_doc, payment_account=None):
|
|||||||
elif dt in ["Sales Invoice", "Purchase Invoice"]:
|
elif dt in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
if not ref_doc.get("is_pos"):
|
if not ref_doc.get("is_pos"):
|
||||||
if ref_doc.party_account_currency == ref_doc.currency:
|
if ref_doc.party_account_currency == ref_doc.currency:
|
||||||
grand_total = flt(ref_doc.outstanding_amount)
|
grand_total = flt(ref_doc.grand_total)
|
||||||
else:
|
else:
|
||||||
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
grand_total = flt(ref_doc.base_grand_total) / ref_doc.conversion_rate
|
||||||
elif dt == "Sales Invoice":
|
elif dt == "Sales Invoice":
|
||||||
for pay in ref_doc.payments:
|
for pay in ref_doc.payments:
|
||||||
if pay.type == "Phone" and pay.account == payment_account:
|
if pay.type == "Phone" and pay.account == payment_account:
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ class TestPaymentRequest(unittest.TestCase):
|
|||||||
pr = make_payment_request(
|
pr = make_payment_request(
|
||||||
dt="Purchase Invoice",
|
dt="Purchase Invoice",
|
||||||
dn=si_usd.name,
|
dn=si_usd.name,
|
||||||
|
party_type="Supplier",
|
||||||
|
party="_Test Supplier USD",
|
||||||
recipient_id="user@example.com",
|
recipient_id="user@example.com",
|
||||||
mute_email=1,
|
mute_email=1,
|
||||||
payment_gateway_account="_Test Gateway - USD",
|
payment_gateway_account="_Test Gateway - USD",
|
||||||
@@ -98,6 +100,51 @@ class TestPaymentRequest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(pr.status, "Paid")
|
self.assertEqual(pr.status, "Paid")
|
||||||
|
|
||||||
|
def test_multiple_payment_entry_against_purchase_invoice(self):
|
||||||
|
purchase_invoice = make_purchase_invoice(
|
||||||
|
customer="_Test Supplier USD",
|
||||||
|
debit_to="_Test Payable USD - _TC",
|
||||||
|
currency="USD",
|
||||||
|
conversion_rate=50,
|
||||||
|
)
|
||||||
|
|
||||||
|
pr = make_payment_request(
|
||||||
|
dt="Purchase Invoice",
|
||||||
|
party_type="Supplier",
|
||||||
|
party="_Test Supplier USD",
|
||||||
|
dn=purchase_invoice.name,
|
||||||
|
recipient_id="user@example.com",
|
||||||
|
mute_email=1,
|
||||||
|
payment_gateway_account="_Test Gateway - USD",
|
||||||
|
return_doc=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
pr.grand_total = pr.grand_total / 2
|
||||||
|
|
||||||
|
pr.submit()
|
||||||
|
pr.create_payment_entry()
|
||||||
|
|
||||||
|
purchase_invoice.load_from_db()
|
||||||
|
self.assertEqual(purchase_invoice.status, "Partly Paid")
|
||||||
|
|
||||||
|
pr = make_payment_request(
|
||||||
|
dt="Purchase Invoice",
|
||||||
|
party_type="Supplier",
|
||||||
|
party="_Test Supplier USD",
|
||||||
|
dn=purchase_invoice.name,
|
||||||
|
recipient_id="user@example.com",
|
||||||
|
mute_email=1,
|
||||||
|
payment_gateway_account="_Test Gateway - USD",
|
||||||
|
return_doc=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
pr.save()
|
||||||
|
pr.submit()
|
||||||
|
pr.create_payment_entry()
|
||||||
|
|
||||||
|
purchase_invoice.load_from_db()
|
||||||
|
self.assertEqual(purchase_invoice.status, "Paid")
|
||||||
|
|
||||||
def test_payment_entry(self):
|
def test_payment_entry(self):
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
|
"Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
|
||||||
|
|||||||
@@ -452,7 +452,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"max_attachments": 4,
|
"max_attachments": 4,
|
||||||
"modified": "2023-02-14 04:54:25.819620",
|
"modified": "2024-04-24 10:56:16.001032",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Projects",
|
"module": "Projects",
|
||||||
"name": "Project",
|
"name": "Project",
|
||||||
@@ -487,6 +487,15 @@
|
|||||||
"role": "Projects Manager",
|
"role": "Projects Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Employee",
|
||||||
|
"select": 1,
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
|
|||||||
@@ -823,11 +823,14 @@ erpnext.utils.map_current_doc = function (opts) {
|
|||||||
if (opts.source_doctype) {
|
if (opts.source_doctype) {
|
||||||
let data_fields = [];
|
let data_fields = [];
|
||||||
if (["Purchase Receipt", "Delivery Note"].includes(opts.source_doctype)) {
|
if (["Purchase Receipt", "Delivery Note"].includes(opts.source_doctype)) {
|
||||||
data_fields.push({
|
let target_meta = frappe.get_meta(cur_frm.doc.doctype);
|
||||||
fieldname: "merge_taxes",
|
if (target_meta.fields.find((f) => f.fieldname === "taxes")) {
|
||||||
fieldtype: "Check",
|
data_fields.push({
|
||||||
label: __("Merge taxes from multiple documents"),
|
fieldname: "merge_taxes",
|
||||||
});
|
fieldtype: "Check",
|
||||||
|
label: __("Merge taxes from multiple documents"),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const d = new frappe.ui.form.MultiSelectDialog({
|
const d = new frappe.ui.form.MultiSelectDialog({
|
||||||
doctype: opts.source_doctype,
|
doctype: opts.source_doctype,
|
||||||
|
|||||||
@@ -135,14 +135,51 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-04-18 08:25:35.302081",
|
"modified": "2024-04-18 15:25:25.808355",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Regional",
|
"module": "Regional",
|
||||||
"name": "Lower Deduction Certificate",
|
"name": "Lower Deduction Certificate",
|
||||||
"naming_rule": "By fieldname",
|
"naming_rule": "By fieldname",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [
|
||||||
"sort_field": "modified",
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
|
|||||||
@@ -18,18 +18,6 @@ erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.fo
|
|||||||
refresh() {
|
refresh() {
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
}
|
}
|
||||||
|
|
||||||
salutation() {
|
|
||||||
if (this.frm.doc.salutation) {
|
|
||||||
this.frm.set_value(
|
|
||||||
"gender",
|
|
||||||
{
|
|
||||||
Mr: "Male",
|
|
||||||
Ms: "Female",
|
|
||||||
}[this.frm.doc.salutation]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
frappe.ui.form.on("Employee", {
|
frappe.ui.form.on("Employee", {
|
||||||
|
|||||||
@@ -772,7 +772,7 @@ def make_delivery_trip(source_name, target_doc=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_installation_note(source_name, target_doc=None):
|
def make_installation_note(source_name, target_doc=None, kwargs=None):
|
||||||
def update_item(obj, target, source_parent):
|
def update_item(obj, target, source_parent):
|
||||||
target.qty = flt(obj.qty) - flt(obj.installed_qty)
|
target.qty = flt(obj.qty) - flt(obj.installed_qty)
|
||||||
target.serial_no = obj.serial_no
|
target.serial_no = obj.serial_no
|
||||||
|
|||||||
@@ -126,8 +126,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self.po_required()
|
self.po_required()
|
||||||
self.validate_items_quality_inspection()
|
self.validate_items_quality_inspection()
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
|
self.validate_uom_is_integer()
|
||||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
|
||||||
self.validate_cwip_accounts()
|
self.validate_cwip_accounts()
|
||||||
self.validate_provisional_expense_account()
|
self.validate_provisional_expense_account()
|
||||||
|
|
||||||
@@ -141,6 +140,10 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
|
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
|
||||||
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
|
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
|
||||||
|
|
||||||
|
def validate_uom_is_integer(self):
|
||||||
|
super().validate_uom_is_integer("uom", ["qty", "received_qty"], "Purchase Receipt Item")
|
||||||
|
super().validate_uom_is_integer("stock_uom", "stock_qty", "Purchase Receipt Item")
|
||||||
|
|
||||||
def validate_cwip_accounts(self):
|
def validate_cwip_accounts(self):
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
|
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.query_reports["Available Batch Report"] = {
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
fieldname: "company",
|
||||||
|
label: __("Company"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Company",
|
||||||
|
default: frappe.defaults.get_default("company"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "to_date",
|
||||||
|
label: __("On This Date"),
|
||||||
|
fieldtype: "Date",
|
||||||
|
width: "80",
|
||||||
|
reqd: 1,
|
||||||
|
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "item_code",
|
||||||
|
label: __("Item"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Item",
|
||||||
|
get_query: () => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
has_batch_no: 1,
|
||||||
|
disabled: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "warehouse",
|
||||||
|
label: __("Warehouse"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Warehouse",
|
||||||
|
get_query: () => {
|
||||||
|
let warehouse_type = frappe.query_report.get_filter_value("warehouse_type");
|
||||||
|
let company = frappe.query_report.get_filter_value("company");
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
...(warehouse_type && { warehouse_type }),
|
||||||
|
...(company && { company }),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "warehouse_type",
|
||||||
|
label: __("Warehouse Type"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Warehouse Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "batch_no",
|
||||||
|
label: __("Batch No"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Batch",
|
||||||
|
get_query: () => {
|
||||||
|
let item = frappe.query_report.get_filter_value("item_code");
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
...(item && { item }),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "include_expired_batches",
|
||||||
|
label: __("Include Expired Batches"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
width: "80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "show_item_name",
|
||||||
|
label: __("Show Item Name"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
width: "80",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 1,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2024-04-11 17:03:32.253275",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"json": "{}",
|
||||||
|
"letter_head": "",
|
||||||
|
"modified": "2024-04-23 17:09:54.595566",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Available Batch Report",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Stock Ledger Entry",
|
||||||
|
"report_name": "Available Batch Report",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Stock User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.query_builder.functions import Sum
|
||||||
|
from frappe.utils import flt, today
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns, data = [], []
|
||||||
|
data = get_data(filters)
|
||||||
|
columns = get_columns(filters)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
def get_columns(filters):
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 200,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
if filters.show_item_name:
|
||||||
|
columns.append(
|
||||||
|
{
|
||||||
|
"label": _("Item Name"),
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 200,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
columns.extend(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": _("Warehouse"),
|
||||||
|
"fieldname": "warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Warehouse",
|
||||||
|
"width": 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Batch No"),
|
||||||
|
"fieldname": "batch_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 150,
|
||||||
|
"options": "Batch",
|
||||||
|
},
|
||||||
|
{"label": _("Balance Qty"), "fieldname": "balance_qty", "fieldtype": "Float", "width": 150},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return columns
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(filters):
|
||||||
|
data = []
|
||||||
|
batchwise_data = get_batchwise_data_from_stock_ledger(filters)
|
||||||
|
|
||||||
|
data = parse_batchwise_data(batchwise_data)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def parse_batchwise_data(batchwise_data):
|
||||||
|
data = []
|
||||||
|
for key in batchwise_data:
|
||||||
|
d = batchwise_data[key]
|
||||||
|
if d.balance_qty == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data.append(d)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_batchwise_data_from_stock_ledger(filters):
|
||||||
|
batchwise_data = frappe._dict({})
|
||||||
|
|
||||||
|
table = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
batch = frappe.qb.DocType("Batch")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(table)
|
||||||
|
.inner_join(batch)
|
||||||
|
.on(table.batch_no == batch.name)
|
||||||
|
.select(
|
||||||
|
table.item_code,
|
||||||
|
table.batch_no,
|
||||||
|
table.warehouse,
|
||||||
|
Sum(table.actual_qty).as_("balance_qty"),
|
||||||
|
)
|
||||||
|
.where(table.is_cancelled == 0)
|
||||||
|
.groupby(table.batch_no, table.item_code, table.warehouse)
|
||||||
|
)
|
||||||
|
|
||||||
|
query = get_query_based_on_filters(query, batch, table, filters)
|
||||||
|
|
||||||
|
for d in query.run(as_dict=True):
|
||||||
|
key = (d.item_code, d.warehouse, d.batch_no)
|
||||||
|
batchwise_data.setdefault(key, d)
|
||||||
|
|
||||||
|
return batchwise_data
|
||||||
|
|
||||||
|
|
||||||
|
def get_query_based_on_filters(query, batch, table, filters):
|
||||||
|
if filters.item_code:
|
||||||
|
query = query.where(table.item_code == filters.item_code)
|
||||||
|
|
||||||
|
if filters.batch_no:
|
||||||
|
query = query.where(batch.name == filters.batch_no)
|
||||||
|
|
||||||
|
if not filters.include_expired_batches:
|
||||||
|
query = query.where((batch.expiry_date >= today()) | (batch.expiry_date.isnull()))
|
||||||
|
if filters.to_date == today():
|
||||||
|
query = query.where(batch.batch_qty > 0)
|
||||||
|
|
||||||
|
if filters.warehouse:
|
||||||
|
lft, rgt = frappe.db.get_value("Warehouse", filters.warehouse, ["lft", "rgt"])
|
||||||
|
warehouses = frappe.get_all(
|
||||||
|
"Warehouse", filters={"lft": (">=", lft), "rgt": ("<=", rgt), "is_group": 0}, pluck="name"
|
||||||
|
)
|
||||||
|
|
||||||
|
query = query.where(table.warehouse.isin(warehouses))
|
||||||
|
|
||||||
|
elif filters.warehouse_type:
|
||||||
|
warehouses = frappe.get_all(
|
||||||
|
"Warehouse", filters={"warehouse_type": filters.warehouse_type, "is_group": 0}, pluck="name"
|
||||||
|
)
|
||||||
|
|
||||||
|
query = query.where(table.warehouse.isin(warehouses))
|
||||||
|
|
||||||
|
if filters.show_item_name:
|
||||||
|
query = query.select(batch.item_name)
|
||||||
|
|
||||||
|
return query
|
||||||
@@ -40,16 +40,26 @@ frappe.query_reports["Batch-Wise Balance History"] = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "warehouse_type",
|
||||||
|
label: __("Warehouse Type"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Warehouse Type",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "warehouse",
|
fieldname: "warehouse",
|
||||||
label: __("Warehouse"),
|
label: __("Warehouse"),
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Warehouse",
|
options: "Warehouse",
|
||||||
get_query: function () {
|
get_query: function () {
|
||||||
let company = frappe.query_report.get_filter_value("company");
|
let warehouse_type = frappe.query_report.get_filter_value("warehouse_type");
|
||||||
|
const company = frappe.query_report.get_filter_value("company");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: company,
|
...(warehouse_type && { warehouse_type }),
|
||||||
|
...(company && { company }),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -113,6 +113,16 @@ def get_stock_ledger_entries(filters):
|
|||||||
)
|
)
|
||||||
|
|
||||||
query = apply_warehouse_filter(query, sle, filters)
|
query = apply_warehouse_filter(query, sle, filters)
|
||||||
|
if filters.warehouse_type and not filters.warehouse:
|
||||||
|
warehouses = frappe.get_all(
|
||||||
|
"Warehouse",
|
||||||
|
filters={"warehouse_type": filters.warehouse_type, "is_group": 0},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
|
||||||
|
if warehouses:
|
||||||
|
query = query.where(sle.warehouse.isin(warehouses))
|
||||||
|
|
||||||
for field in ["item_code", "batch_no", "company"]:
|
for field in ["item_code", "batch_no", "company"]:
|
||||||
if filters.get(field):
|
if filters.get(field):
|
||||||
query = query.where(sle[field] == filters.get(field))
|
query = query.where(sle[field] == filters.get(field))
|
||||||
|
|||||||
@@ -18,15 +18,25 @@ frappe.query_reports["Stock Ageing"] = {
|
|||||||
default: frappe.datetime.get_today(),
|
default: frappe.datetime.get_today(),
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "warehouse_type",
|
||||||
|
label: __("Warehouse Type"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
width: "80",
|
||||||
|
options: "Warehouse Type",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "warehouse",
|
fieldname: "warehouse",
|
||||||
label: __("Warehouse"),
|
label: __("Warehouse"),
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Warehouse",
|
options: "Warehouse",
|
||||||
get_query: () => {
|
get_query: () => {
|
||||||
|
let warehouse_type = frappe.query_report.get_filter_value("warehouse_type");
|
||||||
const company = frappe.query_report.get_filter_value("company");
|
const company = frappe.query_report.get_filter_value("company");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
|
...(warehouse_type && { warehouse_type }),
|
||||||
...(company && { company }),
|
...(company && { company }),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -227,25 +227,30 @@ class FIFOSlots:
|
|||||||
consumed/updated and maintained via FIFO. **
|
consumed/updated and maintained via FIFO. **
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if self.sle is None:
|
stock_ledger_entries = self.sle
|
||||||
self.sle = self.__get_stock_ledger_entries()
|
|
||||||
|
|
||||||
for d in self.sle:
|
with frappe.db.unbuffered_cursor():
|
||||||
key, fifo_queue, transferred_item_key = self.__init_key_stores(d)
|
if self.sle is None:
|
||||||
|
self.sle = self.__get_stock_ledger_entries()
|
||||||
|
|
||||||
if d.voucher_type == "Stock Reconciliation":
|
for d in self.sle:
|
||||||
# get difference in qty shift as actual qty
|
key, fifo_queue, transferred_item_key = self.__init_key_stores(d)
|
||||||
prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
|
|
||||||
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
|
|
||||||
|
|
||||||
serial_nos = get_serial_nos(d.serial_no) if d.serial_no else []
|
if d.voucher_type == "Stock Reconciliation":
|
||||||
|
# get difference in qty shift as actual qty
|
||||||
|
prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
|
||||||
|
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
|
||||||
|
|
||||||
if d.actual_qty > 0:
|
serial_nos = get_serial_nos(d.serial_no) if d.serial_no else []
|
||||||
self.__compute_incoming_stock(d, fifo_queue, transferred_item_key, serial_nos)
|
|
||||||
else:
|
|
||||||
self.__compute_outgoing_stock(d, fifo_queue, transferred_item_key, serial_nos)
|
|
||||||
|
|
||||||
self.__update_balances(d, key)
|
if d.actual_qty > 0:
|
||||||
|
self.__compute_incoming_stock(d, fifo_queue, transferred_item_key, serial_nos)
|
||||||
|
else:
|
||||||
|
self.__compute_outgoing_stock(d, fifo_queue, transferred_item_key, serial_nos)
|
||||||
|
|
||||||
|
self.__update_balances(d, key)
|
||||||
|
|
||||||
|
del stock_ledger_entries
|
||||||
|
|
||||||
if not self.filters.get("show_warehouse_wise_stock"):
|
if not self.filters.get("show_warehouse_wise_stock"):
|
||||||
# (Item 1, WH 1), (Item 1, WH 2) => (Item 1)
|
# (Item 1, WH 1), (Item 1, WH 2) => (Item 1)
|
||||||
@@ -412,10 +417,19 @@ class FIFOSlots:
|
|||||||
|
|
||||||
if self.filters.get("warehouse"):
|
if self.filters.get("warehouse"):
|
||||||
sle_query = self.__get_warehouse_conditions(sle, sle_query)
|
sle_query = self.__get_warehouse_conditions(sle, sle_query)
|
||||||
|
elif self.filters.get("warehouse_type"):
|
||||||
|
warehouses = frappe.get_all(
|
||||||
|
"Warehouse",
|
||||||
|
filters={"warehouse_type": self.filters.get("warehouse_type"), "is_group": 0},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
|
||||||
|
if warehouses:
|
||||||
|
sle_query = sle_query.where(sle.warehouse.isin(warehouses))
|
||||||
|
|
||||||
sle_query = sle_query.orderby(sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty)
|
sle_query = sle_query.orderby(sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty)
|
||||||
|
|
||||||
return sle_query.run(as_dict=True)
|
return sle_query.run(as_dict=True, as_iterator=True)
|
||||||
|
|
||||||
def __get_item_query(self) -> str:
|
def __get_item_query(self) -> str:
|
||||||
item_table = frappe.qb.DocType("Item")
|
item_table = frappe.qb.DocType("Item")
|
||||||
|
|||||||
@@ -329,7 +329,9 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
accepted_warehouse_account = warehouse_account[item.warehouse]["account"]
|
accepted_warehouse_account = warehouse_account[item.warehouse]["account"]
|
||||||
supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account")
|
supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get(
|
||||||
|
"account"
|
||||||
|
)
|
||||||
remarks = self.get("remarks") or _("Accounting Entry for Stock")
|
remarks = self.get("remarks") or _("Accounting Entry for Stock")
|
||||||
|
|
||||||
# Accepted Warehouse Account (Debit)
|
# Accepted Warehouse Account (Debit)
|
||||||
@@ -401,7 +403,9 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if divisional_loss := flt(item.amount - stock_value_diff, item.precision("amount")):
|
if divisional_loss := flt(item.amount - stock_value_diff, item.precision("amount")):
|
||||||
loss_account = self.get_company_default("stock_adjustment_account", ignore_validation=True)
|
loss_account = self.get_company_default(
|
||||||
|
"stock_adjustment_account", ignore_validation=True
|
||||||
|
)
|
||||||
|
|
||||||
# Loss Account (Credit)
|
# Loss Account (Credit)
|
||||||
self.add_gl_entry(
|
self.add_gl_entry(
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ class TransactionBase(StatusUpdater):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
frappe.throw(_("Invalid Posting Time"))
|
frappe.throw(_("Invalid Posting Time"))
|
||||||
|
|
||||||
def validate_uom_is_integer(self, uom_field, qty_fields):
|
def validate_uom_is_integer(self, uom_field, qty_fields, child_dt=None):
|
||||||
validate_uom_is_integer(self, uom_field, qty_fields)
|
validate_uom_is_integer(self, uom_field, qty_fields, child_dt)
|
||||||
|
|
||||||
def validate_with_previous_doc(self, ref):
|
def validate_with_previous_doc(self, ref):
|
||||||
self.exclude_fields = ["conversion_factor", "uom"] if self.get("is_return") else []
|
self.exclude_fields = ["conversion_factor", "uom"] if self.get("is_return") else []
|
||||||
@@ -210,12 +210,13 @@ def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
|
|||||||
for f in qty_fields:
|
for f in qty_fields:
|
||||||
qty = d.get(f)
|
qty = d.get(f)
|
||||||
if qty:
|
if qty:
|
||||||
if abs(cint(qty) - flt(qty, d.precision(f))) > 0.0000001:
|
precision = d.precision(f)
|
||||||
|
if abs(cint(qty) - flt(qty, precision)) > 0.0000001:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}."
|
"Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}."
|
||||||
).format(
|
).format(
|
||||||
flt(qty, d.precision(f)),
|
flt(qty, precision),
|
||||||
d.idx,
|
d.idx,
|
||||||
frappe.bold(_("Must be Whole Number")),
|
frappe.bold(_("Must be Whole Number")),
|
||||||
frappe.bold(d.get(uom_field)),
|
frappe.bold(d.get(uom_field)),
|
||||||
|
|||||||
Reference in New Issue
Block a user