Merge pull request #42542 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
ruthra kumar
2024-07-31 11:29:23 +05:30
committed by GitHub
28 changed files with 181 additions and 130 deletions

View File

@@ -557,7 +557,7 @@
"table_fieldname": "payment_entries" "table_fieldname": "payment_entries"
} }
], ],
"modified": "2023-11-23 12:11:04.128015", "modified": "2024-07-18 15:32:29.413598",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@@ -36,7 +36,7 @@ frappe.ui.form.on("Payment Order", {
// payment Entry // payment Entry
if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") { if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") {
frm.add_custom_button(__("Create Payment Entries"), function () { frm.add_custom_button(__("Create Journal Entries"), function () {
frm.trigger("make_payment_records"); frm.trigger("make_payment_records");
}); });
} }

View File

@@ -59,25 +59,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
this.show_stock_ledger(); this.show_stock_ledger();
} }
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
this.frm.set_intro(__("Accounting entries for this invoice need to be reposted. Please click on 'Repost' button to update."));
this.frm.add_custom_button(__('Repost Accounting Entries'),
() => {
this.frm.call({
doc: this.frm.doc,
method: 'repost_accounting_entries',
freeze: true,
freeze_message: __('Reposting...'),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__('Accounting Entries are reposted.'));
me.frm.refresh();
}
}
});
}).removeClass('btn-default').addClass('btn-warning');
}
if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){ if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){
if(doc.on_hold) { if(doc.on_hold) {
this.frm.add_custom_button( this.frm.add_custom_button(

View File

@@ -170,7 +170,6 @@
"against_expense_account", "against_expense_account",
"column_break_63", "column_break_63",
"unrealized_profit_loss_account", "unrealized_profit_loss_account",
"repost_required",
"subscription_section", "subscription_section",
"auto_repeat", "auto_repeat",
"update_auto_repeat_reference", "update_auto_repeat_reference",
@@ -361,7 +360,8 @@
"description": "Once set, this invoice will be on hold till the set date", "description": "Once set, this invoice will be on hold till the set date",
"fieldname": "release_date", "fieldname": "release_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Release Date" "label": "Release Date",
"search_index": 1
}, },
{ {
"fieldname": "cb_17", "fieldname": "cb_17",
@@ -1590,15 +1590,6 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Use Company Default Round Off Cost Center" "label": "Use Company Default Round Off Cost Center"
}, },
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"options": "Account",
"read_only": 1
},
{ {
"default": "0", "default": "0",
"fieldname": "use_transaction_date_exchange_rate", "fieldname": "use_transaction_date_exchange_rate",
@@ -1619,7 +1610,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 15:57:00.736868", "modified": "2024-07-25 19:42:36.931278",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@@ -590,18 +590,17 @@ class PurchaseInvoice(BuyingController):
self.process_common_party_accounting() self.process_common_party_accounting()
def on_update_after_submit(self): def on_update_after_submit(self):
if hasattr(self, "repost_required"): fields_to_check = [
fields_to_check = [ "cash_bank_account",
"cash_bank_account", "write_off_account",
"write_off_account", "unrealized_profit_loss_account",
"unrealized_profit_loss_account", "is_opening",
] ]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)} child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables) self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost: if self.needs_repost:
self.validate_for_repost() self.validate_for_repost()
self.db_set("repost_required", self.needs_repost) self.repost_accounting_entries()
self.repost_accounting_entries()
def make_gl_entries(self, gl_entries=None, from_repost=False): def make_gl_entries(self, gl_entries=None, from_repost=False):
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes" update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
@@ -1499,6 +1498,9 @@ class PurchaseInvoice(BuyingController):
self.db_set("release_date", None) self.db_set("release_date", None)
def set_tax_withholding(self): def set_tax_withholding(self):
self.set("advance_tax", [])
self.set("tax_withheld_vouchers", [])
if not self.apply_tds: if not self.apply_tds:
return return
@@ -1540,8 +1542,6 @@ class PurchaseInvoice(BuyingController):
self.remove(d) self.remove(d)
## Add pending vouchers on which tax was withheld ## Add pending vouchers on which tax was withheld
self.set("tax_withheld_vouchers", [])
for voucher_no, voucher_details in voucher_wise_amount.items(): for voucher_no, voucher_details in voucher_wise_amount.items():
self.append( self.append(
"tax_withheld_vouchers", "tax_withheld_vouchers",
@@ -1556,7 +1556,6 @@ class PurchaseInvoice(BuyingController):
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
def allocate_advance_tds(self, tax_withholding_details, advance_taxes): def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
self.set("advance_tax", [])
for tax in advance_taxes: for tax in advance_taxes:
allocated_amount = 0 allocated_amount = 0
pending_amount = flt(tax.tax_amount - tax.allocated_amount) pending_amount = flt(tax.tax_amount - tax.allocated_amount)

View File

@@ -1917,8 +1917,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
["Service - _TC", 1000, 0.0, nowdate()], ["Service - _TC", 1000, 0.0, nowdate()],
] ]
check_gl_entries(self, pi.name, expected_gle, nowdate()) check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.load_from_db()
self.assertFalse(pi.repost_required)
def test_default_cost_center_for_purchase(self): def test_default_cost_center_for_purchase(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center

View File

@@ -49,25 +49,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."));
this.frm.add_custom_button(__('Repost Accounting Entries'),
() => {
this.frm.call({
doc: this.frm.doc,
method: 'repost_accounting_entries',
freeze: true,
freeze_message: __('Reposting...'),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__('Accounting Entries are reposted'));
me.frm.refresh();
}
}
});
}).removeClass('btn-default').addClass('btn-warning');
}
if (this.frm.doc.is_return) { if (this.frm.doc.is_return) {
this.frm.return_print_format = "Sales Invoice Return"; this.frm.return_print_format = "Sales Invoice Return";
} }

View File

@@ -213,7 +213,6 @@
"is_internal_customer", "is_internal_customer",
"is_discounted", "is_discounted",
"remarks", "remarks",
"repost_required",
"connections_tab" "connections_tab"
], ],
"fields": [ "fields": [
@@ -2184,7 +2183,7 @@
"link_fieldname": "consolidated_invoice" "link_fieldname": "consolidated_invoice"
} }
], ],
"modified": "2024-05-08 18:02:28.549041", "modified": "2024-07-18 15:30:39.428519",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@@ -385,7 +385,6 @@ class SalesInvoice(SellingController):
self.repost_future_sle_and_gle() self.repost_future_sle_and_gle()
self.db_set("status", "Cancelled") self.db_set("status", "Cancelled")
self.db_set("repost_required", 0)
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction": if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
update_company_current_month_sales(self.company) update_company_current_month_sales(self.company)
@@ -532,24 +531,23 @@ class SalesInvoice(SellingController):
data.sales_invoice = sales_invoice data.sales_invoice = sales_invoice
def on_update_after_submit(self): def on_update_after_submit(self):
if hasattr(self, "repost_required"): fields_to_check = [
fields_to_check = [ "additional_discount_account",
"additional_discount_account", "cash_bank_account",
"cash_bank_account", "account_for_change_amount",
"account_for_change_amount", "write_off_account",
"write_off_account", "loyalty_redemption_account",
"loyalty_redemption_account", "unrealized_profit_loss_account",
"unrealized_profit_loss_account", "is_opening",
] ]
child_tables = { child_tables = {
"items": ("income_account", "expense_account", "discount_account"), "items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",), "taxes": ("account_head",),
} }
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables) self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost: if self.needs_repost:
self.validate_for_repost() self.validate_for_repost()
self.db_set("repost_required", self.needs_repost) self.repost_accounting_entries()
self.repost_accounting_entries()
def set_paid_amount(self): def set_paid_amount(self):
paid_amount = 0.0 paid_amount = 0.0

View File

@@ -2896,9 +2896,6 @@ class TestSalesInvoice(FrappeTestCase):
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
si.load_from_db()
self.assertFalse(si.repost_required)
def test_asset_depreciation_on_sale_with_pro_rata(self): def test_asset_depreciation_on_sale_with_pro_rata(self):
""" """
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale. Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.

View File

@@ -236,6 +236,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
vouchers, voucher_wise_amount = get_invoice_vouchers( vouchers, voucher_wise_amount = get_invoice_vouchers(
parties, tax_details, inv.company, party_type=party_type parties, tax_details, inv.company, party_type=party_type
) )
payment_entry_vouchers = get_payment_entry_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
advance_vouchers = get_advance_vouchers( advance_vouchers = get_advance_vouchers(
parties, parties,
company=inv.company, company=inv.company,
@@ -243,7 +248,8 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
to_date=tax_details.to_date, to_date=tax_details.to_date,
party_type=party_type, party_type=party_type,
) )
taxable_vouchers = vouchers + advance_vouchers
taxable_vouchers = vouchers + advance_vouchers + payment_entry_vouchers
tax_deducted_on_advances = 0 tax_deducted_on_advances = 0
if inv.doctype == "Purchase Invoice": if inv.doctype == "Purchase Invoice":
@@ -355,6 +361,20 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
return vouchers, voucher_wise_amount return vouchers, voucher_wise_amount
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
payment_entry_filters = {
"party_type": party_type,
"party": ("in", parties),
"docstatus": 1,
"apply_tax_withholding_amount": 1,
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": company,
}
return frappe.db.get_all("Payment Entry", filters=payment_entry_filters, pluck="name")
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"): def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
""" """
Use Payment Ledger to fetch unallocated Advance Payments Use Payment Ledger to fetch unallocated Advance Payments

View File

@@ -139,6 +139,7 @@ class ReceivablePayableReport:
paid_in_account_currency=0.0, paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0, credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0, outstanding_in_account_currency=0.0,
cost_center=ple.cost_center,
) )
self.get_invoices(ple) self.get_invoices(ple)
@@ -253,7 +254,7 @@ class ReceivablePayableReport:
row.paid -= amount row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency row.paid_in_account_currency -= amount_in_account_currency
if ple.cost_center: if not row.cost_center and ple.cost_center:
row.cost_center = str(ple.cost_center) row.cost_center = str(ple.cost_center)
def update_sub_total_row(self, row, party): def update_sub_total_row(self, row, party):

View File

@@ -53,11 +53,13 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
si = si.submit() si = si.submit()
return si return si
def create_payment_entry(self, docname): def create_payment_entry(self, docname, do_not_submit=False):
pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40) pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40)
pe.paid_from = self.debit_to pe.paid_from = self.debit_to
pe.insert() pe.insert()
pe.submit() if not do_not_submit:
pe.submit()
return pe
def create_credit_note(self, docname, do_not_submit=False): def create_credit_note(self, docname, do_not_submit=False):
credit_note = create_sales_invoice( credit_note = create_sales_invoice(
@@ -984,3 +986,40 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
expected_data_after_payment, expected_data_after_payment,
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding], [row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
) )
def test_cost_center_on_report_output(self):
filters = {
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.cost_center = self.cost_center
si.save().submit()
new_cc = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "East Wing",
"parent_cost_center": self.company + " - " + self.company_abbr,
"company": self.company,
}
)
new_cc.save()
# check invoice grand total, invoiced, paid and outstanding column's value after payment
pe = self.create_payment_entry(si.name, do_not_submit=True)
pe.cost_center = new_cc.name
pe.save().submit()
report = execute(filters)
expected_data_after_payment = [si.name, si.cost_center, 60]
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual(expected_data_after_payment, [row.voucher_no, row.cost_center, row.outstanding])

View File

@@ -694,7 +694,8 @@ class GrossProfitGenerator:
def get_average_buying_rate(self, row, item_code): def get_average_buying_rate(self, row, item_code):
args = row args = row
if item_code not in self.average_buying_rate: key = (item_code, row.warehouse)
if key not in self.average_buying_rate:
args.update( args.update(
{ {
"voucher_type": row.parenttype, "voucher_type": row.parenttype,
@@ -705,9 +706,9 @@ class GrossProfitGenerator:
) )
average_buying_rate = get_incoming_rate(args) average_buying_rate = get_incoming_rate(args)
self.average_buying_rate[item_code] = flt(average_buying_rate) self.average_buying_rate[key] = flt(average_buying_rate)
return self.average_buying_rate[item_code] return self.average_buying_rate[key]
def get_last_purchase_rate(self, item_code, row): def get_last_purchase_rate(self, item_code, row):
purchase_invoice = frappe.qb.DocType("Purchase Invoice") purchase_invoice = frappe.qb.DocType("Purchase Invoice")

View File

@@ -312,8 +312,9 @@ def apply_conditions(query, pi, pii, filters):
def get_items(filters, additional_table_columns): def get_items(filters, additional_table_columns):
pi = frappe.qb.DocType("Purchase Invoice") doctype = "Purchase Invoice"
pii = frappe.qb.DocType("Purchase Invoice Item") pi = frappe.qb.DocType(doctype)
pii = frappe.qb.DocType(f"{doctype} Item")
Item = frappe.qb.DocType("Item") Item = frappe.qb.DocType("Item")
query = ( query = (
frappe.qb.from_(pi) frappe.qb.from_(pi)
@@ -350,6 +351,7 @@ def get_items(filters, additional_table_columns):
pi.mode_of_payment, pi.mode_of_payment,
) )
.where(pi.docstatus == 1) .where(pi.docstatus == 1)
.where(pii.parenttype == doctype)
) )
if filters.get("supplier"): if filters.get("supplier"):

View File

@@ -407,8 +407,9 @@ def apply_group_by_conditions(query, si, ii, filters):
def get_items(filters, additional_query_columns, additional_conditions=None): def get_items(filters, additional_query_columns, additional_conditions=None):
si = frappe.qb.DocType("Sales Invoice") doctype = "Sales Invoice"
sii = frappe.qb.DocType("Sales Invoice Item") si = frappe.qb.DocType(doctype)
sii = frappe.qb.DocType(f"{doctype} Item")
item = frappe.qb.DocType("Item") item = frappe.qb.DocType("Item")
query = ( query = (
@@ -456,6 +457,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
sii.qty, sii.qty,
) )
.where(si.docstatus == 1) .where(si.docstatus == 1)
.where(sii.parenttype == doctype)
) )
if additional_query_columns: if additional_query_columns:

View File

@@ -1571,6 +1571,18 @@ def auto_create_exchange_rate_revaluation_weekly() -> None:
create_err_and_its_journals(companies) create_err_and_its_journals(companies)
def auto_create_exchange_rate_revaluation_monthly() -> None:
"""
Executed by background job
"""
companies = frappe.db.get_all(
"Company",
filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Montly"},
fields=["name", "submit_err_jv"],
)
create_err_and_its_journals(companies)
def get_payment_ledger_entries(gl_entries, cancel=0): def get_payment_ledger_entries(gl_entries, cancel=0):
ple_map = [] ple_map = []
if gl_entries: if gl_entries:

View File

@@ -43,9 +43,10 @@ def get_data(filters):
query = ( query = (
frappe.qb.from_(po) frappe.qb.from_(po)
.from_(po_item) .inner_join(po_item)
.on(po_item.parent == po.name)
.left_join(pi_item) .left_join(pi_item)
.on(pi_item.po_detail == po_item.name) .on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
.select( .select(
po.transaction_date.as_("date"), po.transaction_date.as_("date"),
po_item.schedule_date.as_("required_date"), po_item.schedule_date.as_("required_date"),

View File

@@ -2376,16 +2376,12 @@ class AccountsController(TransactionBase):
@frappe.whitelist() @frappe.whitelist()
def repost_accounting_entries(self): def repost_accounting_entries(self):
if self.repost_required: repost_ledger = frappe.new_doc("Repost Accounting Ledger")
repost_ledger = frappe.new_doc("Repost Accounting Ledger") repost_ledger.company = self.company
repost_ledger.company = self.company repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name}) repost_ledger.flags.ignore_permissions = True
repost_ledger.flags.ignore_permissions = True repost_ledger.insert()
repost_ledger.insert() repost_ledger.submit()
repost_ledger.submit()
self.db_set("repost_required", 0)
else:
frappe.throw(_("No updates pending for reposting"))
@frappe.whitelist() @frappe.whitelist()

View File

@@ -463,6 +463,7 @@ scheduler_events = {
"monthly_long": [ "monthly_long": [
"erpnext.accounts.deferred_revenue.process_deferred_accounting", "erpnext.accounts.deferred_revenue.process_deferred_accounting",
"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans", "erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans",
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_monthly",
], ],
} }

View File

@@ -1645,6 +1645,12 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
apply_price_list(item, reset_plc_conversion) { apply_price_list(item, reset_plc_conversion) {
// We need to reset plc_conversion_rate sometimes because the call to // We need to reset plc_conversion_rate sometimes because the call to
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value // `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
if (this.frm.doc.doctype === "Material Request") {
return;
}
if (!reset_plc_conversion) { if (!reset_plc_conversion) {
this.frm.set_value("plc_conversion_rate", ""); this.frm.set_value("plc_conversion_rate", "");
} }
@@ -1660,7 +1666,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
me.in_apply_price_list = true; me.in_apply_price_list = true;
return this.frm.call({ return this.frm.call({
method: "erpnext.stock.get_item_details.apply_price_list", method: "erpnext.stock.get_item_details.apply_price_list",
args: { args: args }, args: { args: args, doc: me.frm.doc },
callback: function(r) { callback: function(r) {
if (!r.exc) { if (!r.exc) {
frappe.run_serially([ frappe.run_serially([

View File

@@ -698,7 +698,7 @@
"fieldname": "auto_err_frequency", "fieldname": "auto_err_frequency",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Frequency", "label": "Frequency",
"options": "Daily\nWeekly" "options": "Daily\nWeekly\nMonthly"
}, },
{ {
"default": "0", "default": "0",
@@ -712,7 +712,7 @@
"image_field": "company_logo", "image_field": "company_logo",
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2024-05-27 17:32:49.057386", "modified": "2024-07-24 18:17:56.413971",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@@ -162,7 +162,7 @@ def make_taxes_and_charges_template(company_name, doctype, template):
doc.flags.ignore_links = True doc.flags.ignore_links = True
doc.flags.ignore_validate = True doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True doc.flags.ignore_mandatory = True
doc.insert(ignore_permissions=True) doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
return doc return doc
@@ -195,7 +195,7 @@ def make_item_tax_template(company_name, template):
# Ingone validations to make doctypes faster # Ingone validations to make doctypes faster
doc.flags.ignore_links = True doc.flags.ignore_links = True
doc.flags.ignore_validate = True doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True) doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
return doc return doc
@@ -232,7 +232,7 @@ def get_or_create_account(company_name, account):
doc = frappe.get_doc(account) doc = frappe.get_doc(account)
doc.flags.ignore_links = True doc.flags.ignore_links = True
doc.flags.ignore_validate = True doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True, ignore_mandatory=True) doc.insert(ignore_permissions=True, ignore_mandatory=True, ignore_if_duplicate=True)
return doc return doc

View File

@@ -237,9 +237,23 @@ def repost(doc):
doc.log_error("Unable to repost item valuation") doc.log_error("Unable to repost item valuation")
message = frappe.message_log.pop() if frappe.message_log else "" message = frappe.message_log.pop() if frappe.message_log else ""
status = "Failed"
# If failed because of timeout, set status to In Progress
if traceback and "timeout" in traceback.lower():
status = "In Progress"
if traceback: if traceback:
message += "<br>" + "Traceback: <br>" + traceback message += "<br>" + "Traceback: <br>" + traceback
frappe.db.set_value(doc.doctype, doc.name, "error_log", message)
frappe.db.set_value(
doc.doctype,
doc.name,
{
"error_log": message,
"status": status,
},
)
outgoing_email_account = frappe.get_cached_value( outgoing_email_account = frappe.get_cached_value(
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name" "Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
@@ -247,7 +261,6 @@ def repost(doc):
if outgoing_email_account and not isinstance(e, RecoverableErrors): if outgoing_email_account and not isinstance(e, RecoverableErrors):
notify_error_to_stock_managers(doc, message) notify_error_to_stock_managers(doc, message)
doc.set_status("Failed")
finally: finally:
if not frappe.flags.in_test: if not frappe.flags.in_test:
frappe.db.commit() frappe.db.commit()

View File

@@ -1297,7 +1297,7 @@ def get_batch_qty(batch_no, warehouse, item_code):
@frappe.whitelist() @frappe.whitelist()
def apply_price_list(args, as_doc=False): def apply_price_list(args, as_doc=False, doc=None):
"""Apply pricelist on a document-like dict object and return as """Apply pricelist on a document-like dict object and return as
{'parent': dict, 'children': list} {'parent': dict, 'children': list}
@@ -1336,7 +1336,7 @@ def apply_price_list(args, as_doc=False):
for item in item_list: for item in item_list:
args_copy = frappe._dict(args.copy()) args_copy = frappe._dict(args.copy())
args_copy.update(item) args_copy.update(item)
item_details = apply_price_list_on_item(args_copy) item_details = apply_price_list_on_item(args_copy, doc=doc)
children.append(item_details) children.append(item_details)
if as_doc: if as_doc:
@@ -1354,10 +1354,10 @@ def apply_price_list(args, as_doc=False):
return {"parent": parent, "children": children} return {"parent": parent, "children": children}
def apply_price_list_on_item(args): def apply_price_list_on_item(args, doc=None):
item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1) item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1)
item_details = get_price_list_rate(args, item_doc) item_details = get_price_list_rate(args, item_doc)
item_details.update(get_pricing_rule_for_item(args)) item_details.update(get_pricing_rule_for_item(args, doc=doc))
return item_details return item_details

View File

@@ -3,6 +3,14 @@
frappe.query_reports["Product Bundle Balance"] = { frappe.query_reports["Product Bundle Balance"] = {
filters: [ filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1,
},
{ {
fieldname: "date", fieldname: "date",
label: __("Date"), label: __("Date"),

View File

@@ -224,6 +224,9 @@ def get_stock_ledger_entries(filters, items):
.where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items))) .where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items)))
) )
if filters.get("company"):
query = query.where(sle.company == filters.get("company"))
if date := filters.get("date"): if date := filters.get("date"):
query = query.where(sle.posting_date <= date) query = query.where(sle.posting_date <= date)
else: else:
@@ -237,7 +240,7 @@ def get_stock_ledger_entries(filters, items):
if warehouse_details: if warehouse_details:
wh = frappe.qb.DocType("Warehouse") wh = frappe.qb.DocType("Warehouse")
query = query.where( query = query.where(
ExistsCriterion( sle.warehouse.isin(
frappe.qb.from_(wh) frappe.qb.from_(wh)
.select(wh.name) .select(wh.name)
.where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt)) .where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt))

View File

@@ -209,7 +209,9 @@ def repost_future_sle(
) )
affected_transactions.update(obj.affected_transactions) affected_transactions.update(obj.affected_transactions)
distinct_item_warehouses[(args[i].get("item_code"), args[i].get("warehouse"))].reposting_status = True key = (args[i].get("item_code"), args[i].get("warehouse"))
if distinct_item_warehouses.get(key):
distinct_item_warehouses[key].reposting_status = True
if obj.new_items_found: if obj.new_items_found:
for _item_wh, data in distinct_item_warehouses.items(): for _item_wh, data in distinct_item_warehouses.items():