mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-04 12:49:10 +00:00
Merge pull request #36969 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -13,10 +13,11 @@ frappe.ui.form.on("Bank Transaction", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh(frm) {
|
refresh(frm) {
|
||||||
frm.add_custom_button(__('Unreconcile Transaction'), () => {
|
if (!frm.is_dirty() && frm.doc.payment_entries.length > 0) {
|
||||||
frm.call('remove_payment_entries')
|
frm.add_custom_button(__("Unreconcile Transaction"), () => {
|
||||||
.then( () => frm.refresh() );
|
frm.call("remove_payment_entries").then(() => frm.refresh());
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
bank_account: function (frm) {
|
bank_account: function (frm) {
|
||||||
set_bank_statement_filter(frm);
|
set_bank_statement_filter(frm);
|
||||||
|
|||||||
@@ -538,7 +538,9 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
|
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.events.set_unallocated_amount(frm);
|
// set_unallocated_amount is called by below method,
|
||||||
|
// no need trigger separately
|
||||||
|
frm.events.set_total_allocated_amount(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make read only if Accounts Settings doesn't allow stale rates
|
// Make read only if Accounts Settings doesn't allow stale rates
|
||||||
@@ -562,7 +564,9 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
|
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.events.set_unallocated_amount(frm);
|
// set_unallocated_amount is called by below method,
|
||||||
|
// no need trigger separately
|
||||||
|
frm.events.set_total_allocated_amount(frm);
|
||||||
}
|
}
|
||||||
frm.set_paid_amount_based_on_received_amount = false;
|
frm.set_paid_amount_based_on_received_amount = false;
|
||||||
|
|
||||||
@@ -878,12 +882,18 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_total_allocated_amount: function(frm) {
|
set_total_allocated_amount: function(frm) {
|
||||||
|
let exchange_rate = 1;
|
||||||
|
if (frm.doc.payment_type == "Receive") {
|
||||||
|
exchange_rate = frm.doc.source_exchange_rate;
|
||||||
|
} else if (frm.doc.payment_type == "Pay") {
|
||||||
|
exchange_rate = frm.doc.target_exchange_rate;
|
||||||
|
}
|
||||||
var total_allocated_amount = 0.0;
|
var total_allocated_amount = 0.0;
|
||||||
var base_total_allocated_amount = 0.0;
|
var base_total_allocated_amount = 0.0;
|
||||||
$.each(frm.doc.references || [], function(i, row) {
|
$.each(frm.doc.references || [], function(i, row) {
|
||||||
if (row.allocated_amount) {
|
if (row.allocated_amount) {
|
||||||
total_allocated_amount += flt(row.allocated_amount);
|
total_allocated_amount += flt(row.allocated_amount);
|
||||||
base_total_allocated_amount += flt(flt(row.allocated_amount)*flt(row.exchange_rate),
|
base_total_allocated_amount += flt(flt(row.allocated_amount)*flt(exchange_rate),
|
||||||
precision("base_paid_amount"));
|
precision("base_paid_amount"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -809,6 +809,11 @@ class PaymentEntry(AccountsController):
|
|||||||
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
|
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# on rare case, when `exchange_rate` is unset, gain/loss amount is incorrectly calculated
|
||||||
|
# for base currency transactions
|
||||||
|
if d.exchange_rate is None:
|
||||||
|
d.exchange_rate = 1
|
||||||
|
|
||||||
allocated_amount_in_pe_exchange_rate = flt(
|
allocated_amount_in_pe_exchange_rate = flt(
|
||||||
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
|
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class PaymentReconciliation(Document):
|
|||||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||||
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
||||||
{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
|
{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
|
||||||
t2.account_currency as currency
|
t2.account_currency as currency, t2.cost_center as cost_center
|
||||||
from
|
from
|
||||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
where
|
where
|
||||||
@@ -196,6 +196,7 @@ class PaymentReconciliation(Document):
|
|||||||
"amount": -(inv.outstanding_in_account_currency),
|
"amount": -(inv.outstanding_in_account_currency),
|
||||||
"posting_date": inv.posting_date,
|
"posting_date": inv.posting_date,
|
||||||
"currency": inv.currency,
|
"currency": inv.currency,
|
||||||
|
"cost_center": inv.cost_center,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -344,6 +345,7 @@ class PaymentReconciliation(Document):
|
|||||||
"allocated_amount": allocated_amount,
|
"allocated_amount": allocated_amount,
|
||||||
"difference_amount": pay.get("difference_amount"),
|
"difference_amount": pay.get("difference_amount"),
|
||||||
"currency": inv.get("currency"),
|
"currency": inv.get("currency"),
|
||||||
|
"cost_center": pay.get("cost_center"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -418,6 +420,7 @@ class PaymentReconciliation(Document):
|
|||||||
"allocated_amount": flt(row.get("allocated_amount")),
|
"allocated_amount": flt(row.get("allocated_amount")),
|
||||||
"difference_amount": flt(row.get("difference_amount")),
|
"difference_amount": flt(row.get("difference_amount")),
|
||||||
"difference_account": row.get("difference_account"),
|
"difference_account": row.get("difference_account"),
|
||||||
|
"cost_center": row.get("cost_center"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -590,7 +593,7 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
inv.dr_or_cr: abs(inv.allocated_amount),
|
inv.dr_or_cr: abs(inv.allocated_amount),
|
||||||
"reference_type": inv.against_voucher_type,
|
"reference_type": inv.against_voucher_type,
|
||||||
"reference_name": inv.against_voucher,
|
"reference_name": inv.against_voucher,
|
||||||
"cost_center": erpnext.get_default_cost_center(company),
|
"cost_center": inv.cost_center or erpnext.get_default_cost_center(company),
|
||||||
"user_remark": f"{fmt_money(flt(inv.allocated_amount), currency=company_currency)} against {inv.against_voucher}",
|
"user_remark": f"{fmt_money(flt(inv.allocated_amount), currency=company_currency)} against {inv.against_voucher}",
|
||||||
"exchange_rate": inv.exchange_rate,
|
"exchange_rate": inv.exchange_rate,
|
||||||
},
|
},
|
||||||
@@ -605,7 +608,7 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
),
|
),
|
||||||
"reference_type": inv.voucher_type,
|
"reference_type": inv.voucher_type,
|
||||||
"reference_name": inv.voucher_no,
|
"reference_name": inv.voucher_no,
|
||||||
"cost_center": erpnext.get_default_cost_center(company),
|
"cost_center": inv.cost_center or erpnext.get_default_cost_center(company),
|
||||||
"user_remark": f"{fmt_money(flt(inv.allocated_amount), currency=company_currency)} from {inv.voucher_no}",
|
"user_remark": f"{fmt_money(flt(inv.allocated_amount), currency=company_currency)} from {inv.voucher_no}",
|
||||||
"exchange_rate": inv.exchange_rate,
|
"exchange_rate": inv.exchange_rate,
|
||||||
},
|
},
|
||||||
@@ -631,6 +634,7 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
|
|
||||||
create_gain_loss_journal(
|
create_gain_loss_journal(
|
||||||
company,
|
company,
|
||||||
|
today(),
|
||||||
inv.party_type,
|
inv.party_type,
|
||||||
inv.party,
|
inv.party,
|
||||||
inv.account,
|
inv.account,
|
||||||
@@ -644,4 +648,5 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
inv.against_voucher_type,
|
inv.against_voucher_type,
|
||||||
inv.against_voucher,
|
inv.against_voucher,
|
||||||
None,
|
None,
|
||||||
|
inv.cost_center,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
"column_break_7",
|
"column_break_7",
|
||||||
"difference_account",
|
"difference_account",
|
||||||
"exchange_rate",
|
"exchange_rate",
|
||||||
"currency"
|
"currency",
|
||||||
|
"cost_center"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -144,11 +145,17 @@
|
|||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Exchange Rate",
|
"label": "Exchange Rate",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-12-24 21:01:14.882747",
|
"modified": "2023-09-03 07:52:33.684217",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Allocation",
|
"name": "Payment Reconciliation Allocation",
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
"sec_break1",
|
"sec_break1",
|
||||||
"remark",
|
"remark",
|
||||||
"currency",
|
"currency",
|
||||||
"exchange_rate"
|
"exchange_rate",
|
||||||
|
"cost_center"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -98,11 +99,17 @@
|
|||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Exchange Rate"
|
"label": "Exchange Rate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-11-08 18:18:36.268760",
|
"modified": "2023-09-03 07:43:29.965353",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Payment",
|
"name": "Payment Reconciliation Payment",
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
def make_gl_entries(self, get_opening_entries=False):
|
def make_gl_entries(self, get_opening_entries=False):
|
||||||
gl_entries = self.get_gl_entries()
|
gl_entries = self.get_gl_entries()
|
||||||
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
|
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
|
||||||
if len(gl_entries) > 5000:
|
if len(gl_entries + closing_entries) > 3000:
|
||||||
frappe.enqueue(
|
frappe.enqueue(
|
||||||
process_gl_entries,
|
process_gl_entries,
|
||||||
gl_entries=gl_entries,
|
gl_entries=gl_entries,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
import collections
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@@ -43,6 +43,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
self.validate_debit_to_acc()
|
self.validate_debit_to_acc()
|
||||||
self.validate_write_off_account()
|
self.validate_write_off_account()
|
||||||
self.validate_change_amount()
|
self.validate_change_amount()
|
||||||
|
self.validate_duplicate_serial_and_batch_no()
|
||||||
self.validate_change_account()
|
self.validate_change_account()
|
||||||
self.validate_item_cost_centers()
|
self.validate_item_cost_centers()
|
||||||
self.validate_warehouse()
|
self.validate_warehouse()
|
||||||
@@ -155,6 +156,27 @@ class POSInvoice(SalesInvoice):
|
|||||||
title=_("Item Unavailable"),
|
title=_("Item Unavailable"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_duplicate_serial_and_batch_no(self):
|
||||||
|
serial_nos = []
|
||||||
|
batch_nos = []
|
||||||
|
|
||||||
|
for row in self.get("items"):
|
||||||
|
if row.serial_no:
|
||||||
|
serial_nos = row.serial_no.split("\n")
|
||||||
|
|
||||||
|
if row.batch_no and not row.serial_no:
|
||||||
|
batch_nos.append(row.batch_no)
|
||||||
|
|
||||||
|
if serial_nos:
|
||||||
|
for key, value in collections.Counter(serial_nos).items():
|
||||||
|
if value > 1:
|
||||||
|
frappe.throw(_("Duplicate Serial No {0} found").format("key"))
|
||||||
|
|
||||||
|
if batch_nos:
|
||||||
|
for key, value in collections.Counter(batch_nos).items():
|
||||||
|
if value > 1:
|
||||||
|
frappe.throw(_("Duplicate Batch No {0} found").format("key"))
|
||||||
|
|
||||||
def validate_pos_reserved_batch_qty(self, item):
|
def validate_pos_reserved_batch_qty(self, item):
|
||||||
filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no}
|
filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no}
|
||||||
|
|
||||||
|
|||||||
@@ -269,9 +269,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
|
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
|
|
||||||
asset_items = [d.is_fixed_asset for d in self.items if d.is_fixed_asset]
|
asset_received_but_not_billed = None
|
||||||
if len(asset_items) > 0:
|
|
||||||
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
|
|
||||||
|
|
||||||
if self.update_stock:
|
if self.update_stock:
|
||||||
self.validate_item_code()
|
self.validate_item_code()
|
||||||
@@ -365,6 +363,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
item.expense_account = asset_category_account
|
item.expense_account = asset_category_account
|
||||||
elif item.is_fixed_asset and item.pr_detail:
|
elif item.is_fixed_asset and item.pr_detail:
|
||||||
|
if not asset_received_but_not_billed:
|
||||||
|
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
|
||||||
item.expense_account = asset_received_but_not_billed
|
item.expense_account = asset_received_but_not_billed
|
||||||
elif not item.expense_account and for_validate:
|
elif not item.expense_account and for_validate:
|
||||||
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
||||||
@@ -978,8 +978,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_asset_gl_entry(self, gl_entries):
|
def get_asset_gl_entry(self, gl_entries):
|
||||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
arbnb_account = None
|
||||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
eiiav_account = None
|
||||||
|
asset_eiiav_currency = None
|
||||||
|
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.is_fixed_asset:
|
if item.is_fixed_asset:
|
||||||
@@ -991,6 +992,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"Asset Received But Not Billed",
|
"Asset Received But Not Billed",
|
||||||
"Fixed Asset",
|
"Fixed Asset",
|
||||||
]:
|
]:
|
||||||
|
if not arbnb_account:
|
||||||
|
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
||||||
item.expense_account = arbnb_account
|
item.expense_account = arbnb_account
|
||||||
|
|
||||||
if not self.update_stock:
|
if not self.update_stock:
|
||||||
@@ -1013,7 +1016,10 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if item.item_tax_amount:
|
if item.item_tax_amount:
|
||||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
if not eiiav_account or not asset_eiiav_currency:
|
||||||
|
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||||
|
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
{
|
{
|
||||||
@@ -1056,7 +1062,10 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
||||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
if not eiiav_account or not asset_eiiav_currency:
|
||||||
|
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||||
|
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
{
|
{
|
||||||
@@ -1076,47 +1085,46 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# When update stock is checked
|
|
||||||
# Assets are bought through this document then it will be linked to this document
|
# Assets are bought through this document then it will be linked to this document
|
||||||
if self.update_stock:
|
if flt(item.landed_cost_voucher_amount):
|
||||||
if flt(item.landed_cost_voucher_amount):
|
if not eiiav_account:
|
||||||
gl_entries.append(
|
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||||
self.get_gl_dict(
|
|
||||||
{
|
|
||||||
"account": eiiav_account,
|
|
||||||
"against": cwip_account,
|
|
||||||
"cost_center": item.cost_center,
|
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
|
||||||
"credit": flt(item.landed_cost_voucher_amount),
|
|
||||||
"project": item.project or self.project,
|
|
||||||
},
|
|
||||||
item=item,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
{
|
{
|
||||||
"account": cwip_account,
|
"account": eiiav_account,
|
||||||
"against": eiiav_account,
|
"against": cwip_account,
|
||||||
"cost_center": item.cost_center,
|
"cost_center": item.cost_center,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
"debit": flt(item.landed_cost_voucher_amount),
|
"credit": flt(item.landed_cost_voucher_amount),
|
||||||
"project": item.project or self.project,
|
"project": item.project or self.project,
|
||||||
},
|
},
|
||||||
item=item,
|
item=item,
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# update gross amount of assets bought through this document
|
|
||||||
assets = frappe.db.get_all(
|
|
||||||
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
|
||||||
)
|
)
|
||||||
for asset in assets:
|
|
||||||
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
gl_entries.append(
|
||||||
frappe.db.set_value(
|
self.get_gl_dict(
|
||||||
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
|
{
|
||||||
|
"account": cwip_account,
|
||||||
|
"against": eiiav_account,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"debit": flt(item.landed_cost_voucher_amount),
|
||||||
|
"project": item.project or self.project,
|
||||||
|
},
|
||||||
|
item=item,
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# update gross amount of assets bought through this document
|
||||||
|
assets = frappe.db.get_all(
|
||||||
|
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
||||||
|
)
|
||||||
|
for asset in assets:
|
||||||
|
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||||
|
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
|
||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
|
|||||||
@@ -694,3 +694,23 @@ class TestSubscription(unittest.TestCase):
|
|||||||
# Check the currency of the created invoice
|
# Check the currency of the created invoice
|
||||||
currency = frappe.db.get_value("Sales Invoice", subscription.invoices[0].invoice, "currency")
|
currency = frappe.db.get_value("Sales Invoice", subscription.invoices[0].invoice, "currency")
|
||||||
self.assertEqual(currency, "USD")
|
self.assertEqual(currency, "USD")
|
||||||
|
|
||||||
|
def test_plan_rate_for_midmonth_start_date(self):
|
||||||
|
subscription = frappe.new_doc("Subscription")
|
||||||
|
subscription.party_type = "Supplier"
|
||||||
|
subscription.party = "_Test Supplier"
|
||||||
|
subscription.generate_invoice_at_period_start = 1
|
||||||
|
subscription.follow_calendar_months = 1
|
||||||
|
subscription.generate_new_invoices_past_due_date = 1
|
||||||
|
subscription.start_date = "2023-04-08"
|
||||||
|
subscription.end_date = "2024-02-27"
|
||||||
|
subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1})
|
||||||
|
subscription.save()
|
||||||
|
|
||||||
|
subscription.process()
|
||||||
|
|
||||||
|
self.assertEqual(len(subscription.invoices), 1)
|
||||||
|
pi = frappe.get_doc("Purchase Invoice", subscription.invoices[0].invoice)
|
||||||
|
self.assertEqual(pi.total, 55333.33)
|
||||||
|
|
||||||
|
subscription.delete()
|
||||||
|
|||||||
@@ -57,18 +57,17 @@ def get_plan_rate(
|
|||||||
prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
|
prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
|
||||||
|
|
||||||
if prorate:
|
if prorate:
|
||||||
prorate_factor = flt(
|
cost -= plan.cost * get_prorate_factor(start_date, end_date)
|
||||||
date_diff(start_date, get_first_day(start_date))
|
|
||||||
/ date_diff(get_last_day(start_date), get_first_day(start_date)),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
prorate_factor += flt(
|
|
||||||
date_diff(get_last_day(end_date), end_date)
|
|
||||||
/ date_diff(get_last_day(end_date), get_first_day(end_date)),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
cost -= plan.cost * prorate_factor
|
|
||||||
|
|
||||||
return cost
|
return cost
|
||||||
|
|
||||||
|
|
||||||
|
def get_prorate_factor(start_date, end_date):
|
||||||
|
total_days_to_skip = date_diff(start_date, get_first_day(start_date))
|
||||||
|
total_days_in_month = int(get_last_day(start_date).strftime("%d"))
|
||||||
|
prorate_factor = flt(total_days_to_skip / total_days_in_month)
|
||||||
|
|
||||||
|
total_days_to_skip = date_diff(get_last_day(end_date), end_date)
|
||||||
|
total_days_in_month = int(get_last_day(end_date).strftime("%d"))
|
||||||
|
prorate_factor += flt(total_days_to_skip / total_days_in_month)
|
||||||
|
|
||||||
|
return prorate_factor
|
||||||
|
|||||||
@@ -37,24 +37,6 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "supplier",
|
|
||||||
"label": __("Supplier"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Supplier",
|
|
||||||
on_change: () => {
|
|
||||||
var supplier = frappe.query_report.get_filter_value('supplier');
|
|
||||||
if (supplier) {
|
|
||||||
frappe.db.get_value('Supplier', supplier, "tax_id", function(value) {
|
|
||||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
frappe.query_report.set_filter_value('tax_id', "");
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.query_report.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "party_account",
|
"fieldname": "party_account",
|
||||||
"label": __("Payable Account"),
|
"label": __("Payable Account"),
|
||||||
@@ -112,11 +94,38 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Payment Terms Template"
|
"options": "Payment Terms Template"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "party_type",
|
||||||
|
"label": __("Party Type"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Party Type",
|
||||||
|
get_query: () => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'account_type': 'Payable'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
on_change: () => {
|
||||||
|
frappe.query_report.set_filter_value('party', "");
|
||||||
|
let party_type = frappe.query_report.get_filter_value('party_type');
|
||||||
|
frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"party",
|
||||||
|
"label": __("Party"),
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "party_type",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "supplier_group",
|
"fieldname": "supplier_group",
|
||||||
"label": __("Supplier Group"),
|
"label": __("Supplier Group"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group",
|
||||||
|
"hidden": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "group_by_party",
|
"fieldname": "group_by_party",
|
||||||
@@ -133,12 +142,6 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"label": __("Show Remarks"),
|
"label": __("Show Remarks"),
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "tax_id",
|
|
||||||
"label": __("Tax Id"),
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "show_future_payments",
|
"fieldname": "show_future_payments",
|
||||||
"label": __("Show Future Payments"),
|
"label": __("Show Future Payments"),
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
|
from frappe.utils import add_days, flt, getdate, today
|
||||||
|
|
||||||
|
from erpnext import get_default_cost_center
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
from erpnext.accounts.report.accounts_payable.accounts_payable import execute
|
||||||
|
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||||
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.create_company()
|
||||||
|
self.create_customer()
|
||||||
|
self.create_item()
|
||||||
|
self.create_supplier(currency="USD", supplier_name="Test Supplier2")
|
||||||
|
self.create_usd_payable_account()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
frappe.db.rollback()
|
||||||
|
|
||||||
|
def test_accounts_receivable_with_supplier(self):
|
||||||
|
pi = self.create_purchase_invoice(do_not_submit=True)
|
||||||
|
pi.currency = "USD"
|
||||||
|
pi.conversion_rate = 80
|
||||||
|
pi.credit_to = self.creditors_usd
|
||||||
|
pi = pi.save().submit()
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
"company": self.company,
|
||||||
|
"party_type": "Supplier",
|
||||||
|
"party": self.supplier,
|
||||||
|
"report_date": today(),
|
||||||
|
"range1": 30,
|
||||||
|
"range2": 60,
|
||||||
|
"range3": 90,
|
||||||
|
"range4": 120,
|
||||||
|
}
|
||||||
|
|
||||||
|
data = execute(filters)
|
||||||
|
self.assertEqual(data[1][0].get("outstanding"), 300)
|
||||||
|
self.assertEqual(data[1][0].get("currency"), "USD")
|
||||||
|
|
||||||
|
def create_purchase_invoice(self, do_not_submit=False):
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
pi = make_purchase_invoice(
|
||||||
|
item=self.item,
|
||||||
|
company=self.company,
|
||||||
|
supplier=self.supplier,
|
||||||
|
is_return=False,
|
||||||
|
update_stock=False,
|
||||||
|
posting_date=frappe.utils.datetime.date(2021, 5, 1),
|
||||||
|
do_not_save=1,
|
||||||
|
rate=300,
|
||||||
|
price_list_rate=300,
|
||||||
|
qty=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
pi = pi.save()
|
||||||
|
if not do_not_submit:
|
||||||
|
pi = pi.submit()
|
||||||
|
return pi
|
||||||
@@ -46,8 +46,7 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
var customer = frappe.query_report.get_filter_value('customer');
|
var customer = frappe.query_report.get_filter_value('customer');
|
||||||
var company = frappe.query_report.get_filter_value('company');
|
var company = frappe.query_report.get_filter_value('company');
|
||||||
if (customer) {
|
if (customer) {
|
||||||
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name", "payment_terms"], function(value) {
|
frappe.db.get_value('Customer', customer, ["customer_name", "payment_terms"], function(value) {
|
||||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
|
||||||
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
|
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
|
||||||
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
||||||
});
|
});
|
||||||
@@ -59,7 +58,6 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
}
|
}
|
||||||
}, "Customer");
|
}, "Customer");
|
||||||
} else {
|
} else {
|
||||||
frappe.query_report.set_filter_value('tax_id', "");
|
|
||||||
frappe.query_report.set_filter_value('customer_name', "");
|
frappe.query_report.set_filter_value('customer_name', "");
|
||||||
frappe.query_report.set_filter_value('credit_limit', "");
|
frappe.query_report.set_filter_value('credit_limit', "");
|
||||||
frappe.query_report.set_filter_value('payment_terms', "");
|
frappe.query_report.set_filter_value('payment_terms', "");
|
||||||
@@ -172,12 +170,6 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
"label": __("Show Sales Person"),
|
"label": __("Show Sales Person"),
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "tax_id",
|
|
||||||
"label": __("Tax Id"),
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "show_remarks",
|
"fieldname": "show_remarks",
|
||||||
"label": __("Show Remarks"),
|
"label": __("Show Remarks"),
|
||||||
|
|||||||
@@ -211,11 +211,10 @@ class ReceivablePayableReport(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# amount in "Party Currency", if its supplied. If not, amount in company currency
|
# amount in "Party Currency", if its supplied. If not, amount in company currency
|
||||||
for party_type in self.party_type:
|
if self.filters.get("party_type") and self.filters.get("party"):
|
||||||
if self.filters.get(scrub(party_type)):
|
amount = ple.amount_in_account_currency
|
||||||
amount = ple.amount_in_account_currency
|
else:
|
||||||
else:
|
amount = ple.amount
|
||||||
amount = ple.amount
|
|
||||||
amount_in_account_currency = ple.amount_in_account_currency
|
amount_in_account_currency = ple.amount_in_account_currency
|
||||||
|
|
||||||
# update voucher
|
# update voucher
|
||||||
@@ -426,10 +425,9 @@ class ReceivablePayableReport(object):
|
|||||||
# customer / supplier name
|
# customer / supplier name
|
||||||
party_details = self.get_party_details(row.party) or {}
|
party_details = self.get_party_details(row.party) or {}
|
||||||
row.update(party_details)
|
row.update(party_details)
|
||||||
for party_type in self.party_type:
|
|
||||||
if self.filters.get(scrub(party_type)):
|
if self.filters.get("party_type") and self.filters.get("party"):
|
||||||
row.currency = row.account_currency
|
row.currency = row.account_currency
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
row.currency = self.company_currency
|
row.currency = self.company_currency
|
||||||
|
|
||||||
@@ -469,6 +467,10 @@ class ReceivablePayableReport(object):
|
|||||||
original_row = frappe._dict(row)
|
original_row = frappe._dict(row)
|
||||||
row.payment_terms = []
|
row.payment_terms = []
|
||||||
|
|
||||||
|
# Cr Note's don't have Payment Terms
|
||||||
|
if not payment_terms_details:
|
||||||
|
return
|
||||||
|
|
||||||
# Advance allocated during invoicing is not considered in payment terms
|
# Advance allocated during invoicing is not considered in payment terms
|
||||||
# Deduct that from paid amount pre allocation
|
# Deduct that from paid amount pre allocation
|
||||||
row.paid -= flt(payment_terms_details[0].total_advance)
|
row.paid -= flt(payment_terms_details[0].total_advance)
|
||||||
@@ -765,6 +767,7 @@ class ReceivablePayableReport(object):
|
|||||||
def prepare_conditions(self):
|
def prepare_conditions(self):
|
||||||
self.qb_selection_filter = []
|
self.qb_selection_filter = []
|
||||||
self.or_filters = []
|
self.or_filters = []
|
||||||
|
|
||||||
for party_type in self.party_type:
|
for party_type in self.party_type:
|
||||||
party_type_field = scrub(party_type)
|
party_type_field = scrub(party_type)
|
||||||
self.or_filters.append(self.ple.party_type == party_type)
|
self.or_filters.append(self.ple.party_type == party_type)
|
||||||
@@ -800,6 +803,12 @@ class ReceivablePayableReport(object):
|
|||||||
if self.filters.get(party_type_field):
|
if self.filters.get(party_type_field):
|
||||||
self.qb_selection_filter.append(self.ple.party == self.filters.get(party_type_field))
|
self.qb_selection_filter.append(self.ple.party == self.filters.get(party_type_field))
|
||||||
|
|
||||||
|
if self.filters.get("party_type"):
|
||||||
|
self.qb_selection_filter.append(self.filters.party_type == self.ple.party_type)
|
||||||
|
|
||||||
|
if self.filters.get("party"):
|
||||||
|
self.qb_selection_filter.append(self.filters.party == self.ple.party)
|
||||||
|
|
||||||
if self.filters.party_account:
|
if self.filters.party_account:
|
||||||
self.qb_selection_filter.append(self.ple.account == self.filters.party_account)
|
self.qb_selection_filter.append(self.ple.account == self.filters.party_account)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -324,12 +324,22 @@ def get_journal_entry_party_map(journal_entries):
|
|||||||
|
|
||||||
|
|
||||||
def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
|
def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
|
||||||
common_fields = ["name", "tax_withholding_category"]
|
common_fields = ["name"]
|
||||||
fields_dict = {
|
fields_dict = {
|
||||||
"Purchase Invoice": ["base_tax_withholding_net_total", "grand_total", "base_total"],
|
"Purchase Invoice": [
|
||||||
|
"tax_withholding_category",
|
||||||
|
"base_tax_withholding_net_total",
|
||||||
|
"grand_total",
|
||||||
|
"base_total",
|
||||||
|
],
|
||||||
"Sales Invoice": ["base_net_total", "grand_total", "base_total"],
|
"Sales Invoice": ["base_net_total", "grand_total", "base_total"],
|
||||||
"Payment Entry": ["paid_amount", "paid_amount_after_tax", "base_paid_amount"],
|
"Payment Entry": [
|
||||||
"Journal Entry": ["total_amount"],
|
"tax_withholding_category",
|
||||||
|
"paid_amount",
|
||||||
|
"paid_amount_after_tax",
|
||||||
|
"base_paid_amount",
|
||||||
|
],
|
||||||
|
"Journal Entry": ["tax_withholding_category", "total_amount"],
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = frappe.get_all(
|
entries = frappe.get_all(
|
||||||
|
|||||||
@@ -126,6 +126,28 @@ class AccountsTestMixin:
|
|||||||
acc = frappe.get_doc("Account", name)
|
acc = frappe.get_doc("Account", name)
|
||||||
self.debtors_usd = acc.name
|
self.debtors_usd = acc.name
|
||||||
|
|
||||||
|
def create_usd_payable_account(self):
|
||||||
|
account_name = "Creditors USD"
|
||||||
|
if not frappe.db.get_value(
|
||||||
|
"Account", filters={"account_name": account_name, "company": self.company}
|
||||||
|
):
|
||||||
|
acc = frappe.new_doc("Account")
|
||||||
|
acc.account_name = account_name
|
||||||
|
acc.parent_account = "Accounts Payable - " + self.company_abbr
|
||||||
|
acc.company = self.company
|
||||||
|
acc.account_currency = "USD"
|
||||||
|
acc.account_type = "Payable"
|
||||||
|
acc.insert()
|
||||||
|
else:
|
||||||
|
name = frappe.db.get_value(
|
||||||
|
"Account",
|
||||||
|
filters={"account_name": account_name, "company": self.company},
|
||||||
|
fieldname="name",
|
||||||
|
pluck=True,
|
||||||
|
)
|
||||||
|
acc = frappe.get_doc("Account", name)
|
||||||
|
self.creditors_usd = acc.name
|
||||||
|
|
||||||
def clear_old_entries(self):
|
def clear_old_entries(self):
|
||||||
doctype_list = [
|
doctype_list = [
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
|
|||||||
@@ -458,10 +458,12 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
|
|||||||
|
|
||||||
# update ref in advance entry
|
# update ref in advance entry
|
||||||
if voucher_type == "Journal Entry":
|
if voucher_type == "Journal Entry":
|
||||||
update_reference_in_journal_entry(entry, doc, do_not_save=True)
|
referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False)
|
||||||
# advance section in sales/purchase invoice and reconciliation tool,both pass on exchange gain/loss
|
# advance section in sales/purchase invoice and reconciliation tool,both pass on exchange gain/loss
|
||||||
# amount and account in args
|
# amount and account in args
|
||||||
doc.make_exchange_gain_loss_journal(args)
|
# referenced_row is used to deduplicate gain/loss journal
|
||||||
|
entry.update({"referenced_row": referenced_row})
|
||||||
|
doc.make_exchange_gain_loss_journal([entry])
|
||||||
else:
|
else:
|
||||||
update_reference_in_payment_entry(
|
update_reference_in_payment_entry(
|
||||||
entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
|
entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
|
||||||
@@ -605,6 +607,8 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
|||||||
if not do_not_save:
|
if not do_not_save:
|
||||||
journal_entry.save(ignore_permissions=True)
|
journal_entry.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return new_row.name
|
||||||
|
|
||||||
|
|
||||||
def update_reference_in_payment_entry(
|
def update_reference_in_payment_entry(
|
||||||
d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False
|
d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False
|
||||||
@@ -1720,6 +1724,7 @@ class QueryPaymentLedger(object):
|
|||||||
ple.posting_date,
|
ple.posting_date,
|
||||||
ple.due_date,
|
ple.due_date,
|
||||||
ple.account_currency.as_("currency"),
|
ple.account_currency.as_("currency"),
|
||||||
|
ple.cost_center.as_("cost_center"),
|
||||||
Sum(ple.amount).as_("amount"),
|
Sum(ple.amount).as_("amount"),
|
||||||
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
|
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
|
||||||
)
|
)
|
||||||
@@ -1782,6 +1787,7 @@ class QueryPaymentLedger(object):
|
|||||||
).as_("paid_amount_in_account_currency"),
|
).as_("paid_amount_in_account_currency"),
|
||||||
Table("vouchers").due_date,
|
Table("vouchers").due_date,
|
||||||
Table("vouchers").currency,
|
Table("vouchers").currency,
|
||||||
|
Table("vouchers").cost_center.as_("cost_center"),
|
||||||
)
|
)
|
||||||
.where(Criterion.all(filter_on_outstanding_amount))
|
.where(Criterion.all(filter_on_outstanding_amount))
|
||||||
)
|
)
|
||||||
@@ -1852,6 +1858,7 @@ class QueryPaymentLedger(object):
|
|||||||
|
|
||||||
def create_gain_loss_journal(
|
def create_gain_loss_journal(
|
||||||
company,
|
company,
|
||||||
|
posting_date,
|
||||||
party_type,
|
party_type,
|
||||||
party,
|
party,
|
||||||
party_account,
|
party_account,
|
||||||
@@ -1865,12 +1872,14 @@ def create_gain_loss_journal(
|
|||||||
ref2_dt,
|
ref2_dt,
|
||||||
ref2_dn,
|
ref2_dn,
|
||||||
ref2_detail_no,
|
ref2_detail_no,
|
||||||
|
cost_center,
|
||||||
) -> str:
|
) -> str:
|
||||||
journal_entry = frappe.new_doc("Journal Entry")
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
journal_entry.voucher_type = "Exchange Gain Or Loss"
|
journal_entry.voucher_type = "Exchange Gain Or Loss"
|
||||||
journal_entry.company = company
|
journal_entry.company = company
|
||||||
journal_entry.posting_date = nowdate()
|
journal_entry.posting_date = posting_date or nowdate()
|
||||||
journal_entry.multi_currency = 1
|
journal_entry.multi_currency = 1
|
||||||
|
journal_entry.is_system_generated = True
|
||||||
|
|
||||||
party_account_currency = frappe.get_cached_value("Account", party_account, "account_currency")
|
party_account_currency = frappe.get_cached_value("Account", party_account, "account_currency")
|
||||||
|
|
||||||
@@ -1889,7 +1898,7 @@ def create_gain_loss_journal(
|
|||||||
"party": party,
|
"party": party,
|
||||||
"account_currency": party_account_currency,
|
"account_currency": party_account_currency,
|
||||||
"exchange_rate": 0,
|
"exchange_rate": 0,
|
||||||
"cost_center": erpnext.get_default_cost_center(company),
|
"cost_center": cost_center or erpnext.get_default_cost_center(company),
|
||||||
"reference_type": ref1_dt,
|
"reference_type": ref1_dt,
|
||||||
"reference_name": ref1_dn,
|
"reference_name": ref1_dn,
|
||||||
"reference_detail_no": ref1_detail_no,
|
"reference_detail_no": ref1_detail_no,
|
||||||
@@ -1905,7 +1914,7 @@ def create_gain_loss_journal(
|
|||||||
"account": gain_loss_account,
|
"account": gain_loss_account,
|
||||||
"account_currency": gain_loss_account_currency,
|
"account_currency": gain_loss_account_currency,
|
||||||
"exchange_rate": 1,
|
"exchange_rate": 1,
|
||||||
"cost_center": erpnext.get_default_cost_center(company),
|
"cost_center": cost_center or erpnext.get_default_cost_center(company),
|
||||||
"reference_type": ref2_dt,
|
"reference_type": ref2_dt,
|
||||||
"reference_name": ref2_dn,
|
"reference_name": ref2_dn,
|
||||||
"reference_detail_no": ref2_detail_no,
|
"reference_detail_no": ref2_detail_no,
|
||||||
|
|||||||
@@ -399,14 +399,11 @@ class AssetCapitalization(StockController):
|
|||||||
def get_gl_entries_for_consumed_asset_items(
|
def get_gl_entries_for_consumed_asset_items(
|
||||||
self, gl_entries, target_account, target_against, precision
|
self, gl_entries, target_account, target_against, precision
|
||||||
):
|
):
|
||||||
self.are_all_asset_items_non_depreciable = True
|
|
||||||
|
|
||||||
# Consumed Assets
|
# Consumed Assets
|
||||||
for item in self.asset_items:
|
for item in self.asset_items:
|
||||||
asset = frappe.get_doc("Asset", item.asset)
|
asset = frappe.get_doc("Asset", item.asset)
|
||||||
|
|
||||||
if asset.calculate_depreciation:
|
if asset.calculate_depreciation:
|
||||||
self.are_all_asset_items_non_depreciable = False
|
|
||||||
depreciate_asset(asset, self.posting_date)
|
depreciate_asset(asset, self.posting_date)
|
||||||
asset.reload()
|
asset.reload()
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "parent_location",
|
"fieldname": "parent_location",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Parent Location",
|
"label": "Parent Location",
|
||||||
"options": "Location",
|
"options": "Location",
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
@@ -141,11 +142,11 @@
|
|||||||
],
|
],
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-08 16:11:11.375701",
|
"modified": "2023-08-29 12:49:33.290527",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Location",
|
"name": "Location",
|
||||||
"name_case": "Title Case",
|
"naming_rule": "By fieldname",
|
||||||
"nsm_parent_field": "parent_location",
|
"nsm_parent_field": "parent_location",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@@ -224,5 +225,6 @@
|
|||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
"col_break_email_1",
|
"col_break_email_1",
|
||||||
"html_llwp",
|
"html_llwp",
|
||||||
"send_attached_files",
|
"send_attached_files",
|
||||||
|
"send_document_print",
|
||||||
"sec_break_email_2",
|
"sec_break_email_2",
|
||||||
"message_for_supplier",
|
"message_for_supplier",
|
||||||
"terms_section_break",
|
"terms_section_break",
|
||||||
@@ -283,13 +284,21 @@
|
|||||||
"fieldname": "send_attached_files",
|
"fieldname": "send_attached_files",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Send Attached Files"
|
"label": "Send Attached Files"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "If enabled, a print of this document will be attached to each email",
|
||||||
|
"fieldname": "send_document_print",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Send Document Print",
|
||||||
|
"print_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-08-08 16:30:10.870429",
|
"modified": "2023-08-09 12:20:26.850623",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Request for Quotation",
|
"name": "Request for Quotation",
|
||||||
|
|||||||
@@ -205,10 +205,24 @@ class RequestforQuotation(BuyingController):
|
|||||||
if preview:
|
if preview:
|
||||||
return {"message": message, "subject": subject}
|
return {"message": message, "subject": subject}
|
||||||
|
|
||||||
attachments = None
|
attachments = []
|
||||||
if self.send_attached_files:
|
if self.send_attached_files:
|
||||||
attachments = self.get_attachments()
|
attachments = self.get_attachments()
|
||||||
|
|
||||||
|
if self.send_document_print:
|
||||||
|
supplier_language = frappe.db.get_value("Supplier", data.supplier, "language")
|
||||||
|
system_language = frappe.db.get_single_value("System Settings", "language")
|
||||||
|
attachments.append(
|
||||||
|
frappe.attach_print(
|
||||||
|
self.doctype,
|
||||||
|
self.name,
|
||||||
|
doc=self,
|
||||||
|
print_format=self.meta.default_print_format or "Standard",
|
||||||
|
lang=supplier_language or system_language,
|
||||||
|
letterhead=self.letter_head,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.send_email(data, sender, subject, message, attachments)
|
self.send_email(data, sender, subject, message, attachments)
|
||||||
|
|
||||||
def send_email(self, data, sender, subject, message, attachments):
|
def send_email(self, data, sender, subject, message, attachments):
|
||||||
@@ -218,7 +232,6 @@ class RequestforQuotation(BuyingController):
|
|||||||
recipients=data.email_id,
|
recipients=data.email_id,
|
||||||
sender=sender,
|
sender=sender,
|
||||||
attachments=attachments,
|
attachments=attachments,
|
||||||
print_format=self.meta.default_print_format or "Standard",
|
|
||||||
send_email=True,
|
send_email=True,
|
||||||
doctype=self.doctype,
|
doctype=self.doctype,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
|
|||||||
@@ -969,6 +969,44 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
d.exchange_gain_loss = difference
|
d.exchange_gain_loss = difference
|
||||||
|
|
||||||
|
def gain_loss_journal_already_booked(
|
||||||
|
self,
|
||||||
|
gain_loss_account,
|
||||||
|
exc_gain_loss,
|
||||||
|
ref2_dt,
|
||||||
|
ref2_dn,
|
||||||
|
ref2_detail_no,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Check if gain/loss is booked
|
||||||
|
"""
|
||||||
|
if res := frappe.db.get_all(
|
||||||
|
"Journal Entry Account",
|
||||||
|
filters={
|
||||||
|
"docstatus": 1,
|
||||||
|
"account": gain_loss_account,
|
||||||
|
"reference_type": ref2_dt, # this will be Journal Entry
|
||||||
|
"reference_name": ref2_dn,
|
||||||
|
"reference_detail_no": ref2_detail_no,
|
||||||
|
},
|
||||||
|
pluck="parent",
|
||||||
|
):
|
||||||
|
# deduplicate
|
||||||
|
res = list({x for x in res})
|
||||||
|
if exc_vouchers := frappe.db.get_all(
|
||||||
|
"Journal Entry",
|
||||||
|
filters={"name": ["in", res], "voucher_type": "Exchange Gain Or Loss"},
|
||||||
|
fields=["voucher_type", "total_debit", "total_credit"],
|
||||||
|
):
|
||||||
|
booked_voucher = exc_vouchers[0]
|
||||||
|
if (
|
||||||
|
booked_voucher.total_debit == exc_gain_loss
|
||||||
|
and booked_voucher.total_credit == exc_gain_loss
|
||||||
|
and booked_voucher.voucher_type == "Exchange Gain Or Loss"
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def make_exchange_gain_loss_journal(self, args: dict = None) -> None:
|
def make_exchange_gain_loss_journal(self, args: dict = None) -> None:
|
||||||
"""
|
"""
|
||||||
Make Exchange Gain/Loss journal for Invoices and Payments
|
Make Exchange Gain/Loss journal for Invoices and Payments
|
||||||
@@ -997,27 +1035,37 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
||||||
|
|
||||||
je = create_gain_loss_journal(
|
if not self.gain_loss_journal_already_booked(
|
||||||
self.company,
|
|
||||||
arg.get("party_type"),
|
|
||||||
arg.get("party"),
|
|
||||||
party_account,
|
|
||||||
gain_loss_account,
|
gain_loss_account,
|
||||||
difference_amount,
|
difference_amount,
|
||||||
dr_or_cr,
|
|
||||||
reverse_dr_or_cr,
|
|
||||||
arg.get("against_voucher_type"),
|
|
||||||
arg.get("against_voucher"),
|
|
||||||
arg.get("idx"),
|
|
||||||
self.doctype,
|
self.doctype,
|
||||||
self.name,
|
self.name,
|
||||||
arg.get("idx"),
|
arg.get("referenced_row"),
|
||||||
)
|
):
|
||||||
frappe.msgprint(
|
posting_date = frappe.db.get_value(arg.voucher_type, arg.voucher_no, "posting_date")
|
||||||
_("Exchange Gain/Loss amount has been booked through {0}").format(
|
je = create_gain_loss_journal(
|
||||||
get_link_to_form("Journal Entry", je)
|
self.company,
|
||||||
|
posting_date,
|
||||||
|
arg.get("party_type"),
|
||||||
|
arg.get("party"),
|
||||||
|
party_account,
|
||||||
|
gain_loss_account,
|
||||||
|
difference_amount,
|
||||||
|
dr_or_cr,
|
||||||
|
reverse_dr_or_cr,
|
||||||
|
arg.get("against_voucher_type"),
|
||||||
|
arg.get("against_voucher"),
|
||||||
|
arg.get("idx"),
|
||||||
|
self.doctype,
|
||||||
|
self.name,
|
||||||
|
arg.get("referenced_row"),
|
||||||
|
arg.get("cost_center"),
|
||||||
|
)
|
||||||
|
frappe.msgprint(
|
||||||
|
_("Exchange Gain/Loss amount has been booked through {0}").format(
|
||||||
|
get_link_to_form("Journal Entry", je)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
if self.get("doctype") == "Payment Entry":
|
if self.get("doctype") == "Payment Entry":
|
||||||
# For Payment Entry, exchange_gain_loss field in the `references` table is the trigger for journal creation
|
# For Payment Entry, exchange_gain_loss field in the `references` table is the trigger for journal creation
|
||||||
@@ -1077,6 +1125,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
je = create_gain_loss_journal(
|
je = create_gain_loss_journal(
|
||||||
self.company,
|
self.company,
|
||||||
|
self.posting_date,
|
||||||
self.party_type,
|
self.party_type,
|
||||||
self.party,
|
self.party,
|
||||||
party_account,
|
party_account,
|
||||||
@@ -1090,6 +1139,7 @@ class AccountsController(TransactionBase):
|
|||||||
self.doctype,
|
self.doctype,
|
||||||
self.name,
|
self.name,
|
||||||
d.idx,
|
d.idx,
|
||||||
|
self.cost_center,
|
||||||
)
|
)
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
_("Exchange Gain/Loss amount has been booked through {0}").format(
|
_("Exchange Gain/Loss amount has been booked through {0}").format(
|
||||||
@@ -1354,7 +1404,7 @@ class AccountsController(TransactionBase):
|
|||||||
{
|
{
|
||||||
"account": self.additional_discount_account,
|
"account": self.additional_discount_account,
|
||||||
"against": supplier_or_customer,
|
"against": supplier_or_customer,
|
||||||
dr_or_cr: self.discount_amount,
|
dr_or_cr: self.base_discount_amount,
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
},
|
},
|
||||||
item=self,
|
item=self,
|
||||||
@@ -1626,6 +1676,7 @@ class AccountsController(TransactionBase):
|
|||||||
and party_account_currency != self.company_currency
|
and party_account_currency != self.company_currency
|
||||||
and self.currency != party_account_currency
|
and self.currency != party_account_currency
|
||||||
):
|
):
|
||||||
|
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Accounting Entry for {0}: {1} can only be made in currency: {2}").format(
|
_("Accounting Entry for {0}: {1} can only be made in currency: {2}").format(
|
||||||
party_type, party, party_account_currency
|
party_type, party, party_account_currency
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class TestAccountsController(FrappeTestCase):
|
|||||||
10 series - Sales Invoice against Payment Entries
|
10 series - Sales Invoice against Payment Entries
|
||||||
20 series - Sales Invoice against Journals
|
20 series - Sales Invoice against Journals
|
||||||
30 series - Sales Invoice against Credit Notes
|
30 series - Sales Invoice against Credit Notes
|
||||||
|
40 series - Company default Cost center is unset
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -941,6 +942,60 @@ class TestAccountsController(FrappeTestCase):
|
|||||||
self.assertEqual(exc_je_for_si, [])
|
self.assertEqual(exc_je_for_si, [])
|
||||||
self.assertEqual(exc_je_for_je, [])
|
self.assertEqual(exc_je_for_je, [])
|
||||||
|
|
||||||
|
def test_24_journal_against_multiple_invoices(self):
|
||||||
|
si1 = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1)
|
||||||
|
si2 = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1)
|
||||||
|
|
||||||
|
# Payment
|
||||||
|
je = self.create_journal_entry(
|
||||||
|
acc1=self.debit_usd,
|
||||||
|
acc1_exc_rate=75,
|
||||||
|
acc2=self.cash,
|
||||||
|
acc1_amount=-2,
|
||||||
|
acc2_amount=-150,
|
||||||
|
acc2_exc_rate=1,
|
||||||
|
)
|
||||||
|
je.accounts[0].party_type = "Customer"
|
||||||
|
je.accounts[0].party = self.customer
|
||||||
|
je = je.save().submit()
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
self.assertEqual(len(pr.invoices), 2)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [x.as_dict() for x in pr.payments]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.reconcile()
|
||||||
|
self.assertEqual(len(pr.invoices), 0)
|
||||||
|
self.assertEqual(len(pr.payments), 0)
|
||||||
|
|
||||||
|
si1.reload()
|
||||||
|
si2.reload()
|
||||||
|
|
||||||
|
self.assertEqual(si1.outstanding_amount, 0)
|
||||||
|
self.assertEqual(si2.outstanding_amount, 0)
|
||||||
|
self.assert_ledger_outstanding(si1.doctype, si1.name, 0.0, 0.0)
|
||||||
|
self.assert_ledger_outstanding(si2.doctype, si2.name, 0.0, 0.0)
|
||||||
|
|
||||||
|
# Exchange Gain/Loss Journal should've been created
|
||||||
|
# remove payment JE from list
|
||||||
|
exc_je_for_si1 = [x for x in self.get_journals_for(si1.doctype, si1.name) if x.parent != je.name]
|
||||||
|
exc_je_for_si2 = [x for x in self.get_journals_for(si2.doctype, si2.name) if x.parent != je.name]
|
||||||
|
exc_je_for_je = [x for x in self.get_journals_for(je.doctype, je.name) if x.parent != je.name]
|
||||||
|
self.assertEqual(len(exc_je_for_si1), 1)
|
||||||
|
self.assertEqual(len(exc_je_for_si2), 1)
|
||||||
|
self.assertEqual(len(exc_je_for_je), 2)
|
||||||
|
|
||||||
|
si1.cancel()
|
||||||
|
# Gain/Loss JE of si1 should've been cancelled
|
||||||
|
exc_je_for_si1 = [x for x in self.get_journals_for(si1.doctype, si1.name) if x.parent != je.name]
|
||||||
|
exc_je_for_si2 = [x for x in self.get_journals_for(si2.doctype, si2.name) if x.parent != je.name]
|
||||||
|
exc_je_for_je = [x for x in self.get_journals_for(je.doctype, je.name) if x.parent != je.name]
|
||||||
|
self.assertEqual(len(exc_je_for_si1), 0)
|
||||||
|
self.assertEqual(len(exc_je_for_si2), 1)
|
||||||
|
self.assertEqual(len(exc_je_for_je), 1)
|
||||||
|
|
||||||
def test_30_cr_note_against_sales_invoice(self):
|
def test_30_cr_note_against_sales_invoice(self):
|
||||||
"""
|
"""
|
||||||
Reconciling Cr Note against Sales Invoice, both having different exchange rates
|
Reconciling Cr Note against Sales Invoice, both having different exchange rates
|
||||||
@@ -997,3 +1052,139 @@ class TestAccountsController(FrappeTestCase):
|
|||||||
si.reload()
|
si.reload()
|
||||||
self.assertEqual(si.outstanding_amount, 1)
|
self.assertEqual(si.outstanding_amount, 1)
|
||||||
self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0)
|
self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0)
|
||||||
|
|
||||||
|
def test_40_cost_center_from_payment_entry(self):
|
||||||
|
"""
|
||||||
|
Gain/Loss JE should inherit cost center from payment if company default is unset
|
||||||
|
"""
|
||||||
|
# remove default cost center
|
||||||
|
cc = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
|
frappe.db.set_value("Company", self.company, "cost_center", None)
|
||||||
|
|
||||||
|
rate_in_account_currency = 1
|
||||||
|
si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
|
||||||
|
si.cost_center = None
|
||||||
|
si.save().submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry(si.doctype, si.name)
|
||||||
|
pe.source_exchange_rate = 75
|
||||||
|
pe.received_amount = 75
|
||||||
|
pe.cost_center = self.cost_center
|
||||||
|
pe = pe.save().submit()
|
||||||
|
|
||||||
|
# Exchange Gain/Loss Journal should've been created.
|
||||||
|
exc_je_for_si = self.get_journals_for(si.doctype, si.name)
|
||||||
|
exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
|
||||||
|
self.assertNotEqual(exc_je_for_si, [])
|
||||||
|
self.assertEqual(len(exc_je_for_si), 1)
|
||||||
|
self.assertEqual(len(exc_je_for_pe), 1)
|
||||||
|
self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[self.cost_center, self.cost_center],
|
||||||
|
frappe.db.get_all(
|
||||||
|
"Journal Entry Account", filters={"parent": exc_je_for_si[0].parent}, pluck="cost_center"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
frappe.db.set_value("Company", self.company, "cost_center", cc)
|
||||||
|
|
||||||
|
def test_41_cost_center_from_journal_entry(self):
|
||||||
|
"""
|
||||||
|
Gain/Loss JE should inherit cost center from payment if company default is unset
|
||||||
|
"""
|
||||||
|
# remove default cost center
|
||||||
|
cc = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
|
frappe.db.set_value("Company", self.company, "cost_center", None)
|
||||||
|
|
||||||
|
rate_in_account_currency = 1
|
||||||
|
si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
|
||||||
|
si.cost_center = None
|
||||||
|
si.save().submit()
|
||||||
|
|
||||||
|
je = self.create_journal_entry(
|
||||||
|
acc1=self.debit_usd,
|
||||||
|
acc1_exc_rate=75,
|
||||||
|
acc2=self.cash,
|
||||||
|
acc1_amount=-1,
|
||||||
|
acc2_amount=-75,
|
||||||
|
acc2_exc_rate=1,
|
||||||
|
)
|
||||||
|
je.accounts[0].party_type = "Customer"
|
||||||
|
je.accounts[0].party = self.customer
|
||||||
|
je.accounts[0].cost_center = self.cost_center
|
||||||
|
je = je.save().submit()
|
||||||
|
|
||||||
|
# Reconcile
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [x.as_dict() for x in pr.payments]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.reconcile()
|
||||||
|
self.assertEqual(len(pr.invoices), 0)
|
||||||
|
self.assertEqual(len(pr.payments), 0)
|
||||||
|
|
||||||
|
# Exchange Gain/Loss Journal should've been created.
|
||||||
|
exc_je_for_si = [x for x in self.get_journals_for(si.doctype, si.name) if x.parent != je.name]
|
||||||
|
exc_je_for_je = [x for x in self.get_journals_for(je.doctype, je.name) if x.parent != je.name]
|
||||||
|
self.assertNotEqual(exc_je_for_si, [])
|
||||||
|
self.assertEqual(len(exc_je_for_si), 1)
|
||||||
|
self.assertEqual(len(exc_je_for_je), 1)
|
||||||
|
self.assertEqual(exc_je_for_si[0], exc_je_for_je[0])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[self.cost_center, self.cost_center],
|
||||||
|
frappe.db.get_all(
|
||||||
|
"Journal Entry Account", filters={"parent": exc_je_for_si[0].parent}, pluck="cost_center"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
frappe.db.set_value("Company", self.company, "cost_center", cc)
|
||||||
|
|
||||||
|
def test_42_cost_center_from_cr_note(self):
|
||||||
|
"""
|
||||||
|
Gain/Loss JE should inherit cost center from payment if company default is unset
|
||||||
|
"""
|
||||||
|
# remove default cost center
|
||||||
|
cc = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
|
frappe.db.set_value("Company", self.company, "cost_center", None)
|
||||||
|
|
||||||
|
rate_in_account_currency = 1
|
||||||
|
si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
|
||||||
|
si.cost_center = None
|
||||||
|
si.save().submit()
|
||||||
|
|
||||||
|
cr_note = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
|
||||||
|
cr_note.cost_center = self.cost_center
|
||||||
|
cr_note.is_return = 1
|
||||||
|
cr_note.save().submit()
|
||||||
|
|
||||||
|
# Reconcile
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [x.as_dict() for x in pr.payments]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.reconcile()
|
||||||
|
self.assertEqual(len(pr.invoices), 0)
|
||||||
|
self.assertEqual(len(pr.payments), 0)
|
||||||
|
|
||||||
|
# Exchange Gain/Loss Journal should've been created.
|
||||||
|
exc_je_for_si = self.get_journals_for(si.doctype, si.name)
|
||||||
|
exc_je_for_cr_note = self.get_journals_for(cr_note.doctype, cr_note.name)
|
||||||
|
self.assertNotEqual(exc_je_for_si, [])
|
||||||
|
self.assertEqual(len(exc_je_for_si), 2)
|
||||||
|
self.assertEqual(len(exc_je_for_cr_note), 2)
|
||||||
|
self.assertEqual(exc_je_for_si, exc_je_for_cr_note)
|
||||||
|
|
||||||
|
for x in exc_je_for_si + exc_je_for_cr_note:
|
||||||
|
with self.subTest(x=x):
|
||||||
|
self.assertEqual(
|
||||||
|
[self.cost_center, self.cost_center],
|
||||||
|
frappe.db.get_all("Journal Entry Account", filters={"parent": x.parent}, pluck="cost_center"),
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value("Company", self.company, "cost_center", cc)
|
||||||
|
|||||||
@@ -263,12 +263,13 @@
|
|||||||
"label": "Accounting Details"
|
"label": "Accounting Details"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.repay_from_salary",
|
||||||
"fetch_from": "against_loan.payment_account",
|
"fetch_from": "against_loan.payment_account",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "payment_account",
|
"fieldname": "payment_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Repayment Account",
|
"label": "Repayment Account",
|
||||||
"options": "Account",
|
"options": "Account"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_36",
|
"fieldname": "column_break_36",
|
||||||
@@ -294,7 +295,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-06-21 10:10:07.742298",
|
"modified": "2023-09-04 15:44:29.148766",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Repayment",
|
"name": "Loan Repayment",
|
||||||
|
|||||||
@@ -338,5 +338,6 @@ erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
|
|||||||
erpnext.patches.v14_0.update_closing_balances #15-07-2023
|
erpnext.patches.v14_0.update_closing_balances #15-07-2023
|
||||||
execute:frappe.defaults.clear_default("fiscal_year")
|
execute:frappe.defaults.clear_default("fiscal_year")
|
||||||
execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for_items', 0)
|
execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for_items', 0)
|
||||||
|
erpnext.patches.v14_0.correct_asset_value_if_je_with_workflow
|
||||||
# below migration patch should always run last
|
# below migration patch should always run last
|
||||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||||
|
|||||||
119
erpnext/patches/v14_0/correct_asset_value_if_je_with_workflow.py
Normal file
119
erpnext/patches/v14_0/correct_asset_value_if_je_with_workflow.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.model.workflow import get_workflow_name
|
||||||
|
from frappe.query_builder.functions import IfNull, Sum
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
active_je_workflow = get_workflow_name("Journal Entry")
|
||||||
|
if not active_je_workflow:
|
||||||
|
return
|
||||||
|
|
||||||
|
correct_value_for_assets_with_manual_depr_entries()
|
||||||
|
|
||||||
|
finance_books = frappe.db.get_all("Finance Book", pluck="name")
|
||||||
|
|
||||||
|
if finance_books:
|
||||||
|
for fb_name in finance_books:
|
||||||
|
correct_value_for_assets_with_auto_depr(fb_name)
|
||||||
|
|
||||||
|
correct_value_for_assets_with_auto_depr()
|
||||||
|
|
||||||
|
|
||||||
|
def correct_value_for_assets_with_manual_depr_entries():
|
||||||
|
asset = frappe.qb.DocType("Asset")
|
||||||
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
|
aca = frappe.qb.DocType("Asset Category Account")
|
||||||
|
company = frappe.qb.DocType("Company")
|
||||||
|
|
||||||
|
asset_details_and_depr_amount_map = (
|
||||||
|
frappe.qb.from_(gle)
|
||||||
|
.join(asset)
|
||||||
|
.on(gle.against_voucher == asset.name)
|
||||||
|
.join(aca)
|
||||||
|
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
|
||||||
|
.join(company)
|
||||||
|
.on(company.name == asset.company)
|
||||||
|
.select(
|
||||||
|
asset.name.as_("asset_name"),
|
||||||
|
asset.gross_purchase_amount.as_("gross_purchase_amount"),
|
||||||
|
asset.opening_accumulated_depreciation.as_("opening_accumulated_depreciation"),
|
||||||
|
Sum(gle.debit).as_("depr_amount"),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
||||||
|
)
|
||||||
|
.where(gle.debit != 0)
|
||||||
|
.where(gle.is_cancelled == 0)
|
||||||
|
.where(asset.docstatus == 1)
|
||||||
|
.where(asset.calculate_depreciation == 0)
|
||||||
|
.groupby(asset.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.qb.update(asset).join(asset_details_and_depr_amount_map).on(
|
||||||
|
asset_details_and_depr_amount_map.asset_name == asset.name
|
||||||
|
).set(
|
||||||
|
asset.value_after_depreciation,
|
||||||
|
asset_details_and_depr_amount_map.gross_purchase_amount
|
||||||
|
- asset_details_and_depr_amount_map.opening_accumulated_depreciation
|
||||||
|
- asset_details_and_depr_amount_map.depr_amount,
|
||||||
|
).run()
|
||||||
|
|
||||||
|
|
||||||
|
def correct_value_for_assets_with_auto_depr(fb_name=None):
|
||||||
|
asset = frappe.qb.DocType("Asset")
|
||||||
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
|
aca = frappe.qb.DocType("Asset Category Account")
|
||||||
|
company = frappe.qb.DocType("Company")
|
||||||
|
afb = frappe.qb.DocType("Asset Finance Book")
|
||||||
|
|
||||||
|
asset_details_and_depr_amount_map = (
|
||||||
|
frappe.qb.from_(gle)
|
||||||
|
.join(asset)
|
||||||
|
.on(gle.against_voucher == asset.name)
|
||||||
|
.join(aca)
|
||||||
|
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
|
||||||
|
.join(company)
|
||||||
|
.on(company.name == asset.company)
|
||||||
|
.select(
|
||||||
|
asset.name.as_("asset_name"),
|
||||||
|
asset.gross_purchase_amount.as_("gross_purchase_amount"),
|
||||||
|
asset.opening_accumulated_depreciation.as_("opening_accumulated_depreciation"),
|
||||||
|
Sum(gle.debit).as_("depr_amount"),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
||||||
|
)
|
||||||
|
.where(gle.debit != 0)
|
||||||
|
.where(gle.is_cancelled == 0)
|
||||||
|
.where(asset.docstatus == 1)
|
||||||
|
.where(asset.calculate_depreciation == 1)
|
||||||
|
.groupby(asset.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
if fb_name:
|
||||||
|
asset_details_and_depr_amount_map = asset_details_and_depr_amount_map.where(
|
||||||
|
gle.finance_book == fb_name
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
asset_details_and_depr_amount_map = asset_details_and_depr_amount_map.where(
|
||||||
|
(gle.finance_book.isin([""])) | (gle.finance_book.isnull())
|
||||||
|
)
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.update(afb)
|
||||||
|
.join(asset_details_and_depr_amount_map)
|
||||||
|
.on(asset_details_and_depr_amount_map.asset_name == afb.parent)
|
||||||
|
.set(
|
||||||
|
afb.value_after_depreciation,
|
||||||
|
asset_details_and_depr_amount_map.gross_purchase_amount
|
||||||
|
- asset_details_and_depr_amount_map.opening_accumulated_depreciation
|
||||||
|
- asset_details_and_depr_amount_map.depr_amount,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if fb_name:
|
||||||
|
query = query.where(afb.finance_book == fb_name)
|
||||||
|
else:
|
||||||
|
query = query.where((afb.finance_book.isin([""])) | (afb.finance_book.isnull()))
|
||||||
|
|
||||||
|
query.run()
|
||||||
@@ -28,7 +28,7 @@ erpnext.get_purchase_trends_filters = function() {
|
|||||||
"label": __("Fiscal Year"),
|
"label": __("Fiscal Year"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options":'Fiscal Year',
|
"options":'Fiscal Year',
|
||||||
"default": frappe.sys_defaults.fiscal_year
|
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today())
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"period_based_on",
|
"fieldname":"period_based_on",
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ erpnext.get_sales_trends_filters = function() {
|
|||||||
"label": __("Fiscal Year"),
|
"label": __("Fiscal Year"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options":'Fiscal Year',
|
"options":'Fiscal Year',
|
||||||
"default": frappe.sys_defaults.fiscal_year
|
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today())
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"company",
|
"fieldname":"company",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "parent_quality_procedure",
|
"fieldname": "parent_quality_procedure",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Parent Procedure",
|
"label": "Parent Procedure",
|
||||||
"options": "Quality Procedure"
|
"options": "Quality Procedure"
|
||||||
},
|
},
|
||||||
@@ -115,7 +116,7 @@
|
|||||||
"link_fieldname": "procedure"
|
"link_fieldname": "procedure"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2020-10-26 15:25:39.316088",
|
"modified": "2023-08-29 12:49:53.963370",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Quality Management",
|
"module": "Quality Management",
|
||||||
"name": "Quality Procedure",
|
"name": "Quality Procedure",
|
||||||
@@ -149,5 +150,6 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -25,18 +25,15 @@
|
|||||||
"label": "Department",
|
"label": "Department",
|
||||||
"oldfieldname": "department_name",
|
"oldfieldname": "department_name",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"reqd": 1,
|
"reqd": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "parent_department",
|
"fieldname": "parent_department",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Parent Department",
|
"label": "Parent Department",
|
||||||
"options": "Department",
|
"options": "Department"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
@@ -44,9 +41,7 @@
|
|||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"reqd": 1,
|
"reqd": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
@@ -54,17 +49,13 @@
|
|||||||
"fieldname": "is_group",
|
"fieldname": "is_group",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Is Group",
|
"label": "Is Group"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "disabled",
|
"fieldname": "disabled",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Disabled",
|
"label": "Disabled"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "lft",
|
"fieldname": "lft",
|
||||||
@@ -72,9 +63,7 @@
|
|||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "lft",
|
"label": "lft",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "rgt",
|
"fieldname": "rgt",
|
||||||
@@ -82,9 +71,7 @@
|
|||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "rgt",
|
"label": "rgt",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "old_parent",
|
"fieldname": "old_parent",
|
||||||
@@ -92,22 +79,18 @@
|
|||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"label": "Old Parent",
|
"label": "Old Parent",
|
||||||
"print_hide": 1,
|
"print_hide": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-sitemap",
|
"icon": "fa fa-sitemap",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-10 12:28:00.563272",
|
"modified": "2023-08-28 17:26:46.826501",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Department",
|
"name": "Department",
|
||||||
@@ -147,12 +130,12 @@
|
|||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "HR Manager",
|
"role": "HR Manager",
|
||||||
"set_user_permissions": 1,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC"
|
"sort_order": "ASC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
frappe.provide("erpnext.item");
|
frappe.provide("erpnext.item");
|
||||||
|
|
||||||
|
const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'];
|
||||||
|
const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'];
|
||||||
|
|
||||||
frappe.ui.form.on("Item", {
|
frappe.ui.form.on("Item", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch('attribute', 'numeric_values', 'numeric_values');
|
frm.add_fetch('attribute', 'numeric_values', 'numeric_values');
|
||||||
@@ -894,7 +897,13 @@ function open_form(frm, doctype, child_doctype, parentfield) {
|
|||||||
let new_child_doc = frappe.model.add_child(new_doc, child_doctype, parentfield);
|
let new_child_doc = frappe.model.add_child(new_doc, child_doctype, parentfield);
|
||||||
new_child_doc.item_code = frm.doc.name;
|
new_child_doc.item_code = frm.doc.name;
|
||||||
new_child_doc.item_name = frm.doc.item_name;
|
new_child_doc.item_name = frm.doc.item_name;
|
||||||
new_child_doc.uom = frm.doc.stock_uom;
|
if (in_list(SALES_DOCTYPES, doctype) && frm.doc.sales_uom) {
|
||||||
|
new_child_doc.uom = frm.doc.sales_uom;
|
||||||
|
} else if (in_list(PURCHASE_DOCTYPES, doctype) && frm.doc.purchase_uom) {
|
||||||
|
new_child_doc.uom = frm.doc.purchase_uom;
|
||||||
|
} else {
|
||||||
|
new_child_doc.uom = frm.doc.stock_uom;
|
||||||
|
}
|
||||||
new_child_doc.description = frm.doc.description;
|
new_child_doc.description = frm.doc.description;
|
||||||
if (!new_child_doc.qty) {
|
if (!new_child_doc.qty) {
|
||||||
new_child_doc.qty = 1.0;
|
new_child_doc.qty = 1.0;
|
||||||
|
|||||||
@@ -706,6 +706,7 @@ def raise_work_orders(material_request):
|
|||||||
)
|
)
|
||||||
|
|
||||||
wo_order.set_work_order_operations()
|
wo_order.set_work_order_operations()
|
||||||
|
wo_order.flags.ignore_mandatory = True
|
||||||
wo_order.save()
|
wo_order.save()
|
||||||
|
|
||||||
work_orders.append(wo_order.name)
|
work_orders.append(wo_order.name)
|
||||||
|
|||||||
@@ -176,10 +176,9 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
item.rm_cost_per_qty = item.rm_supp_cost / item.qty
|
item.rm_cost_per_qty = item.rm_supp_cost / item.qty
|
||||||
rm_supp_cost.pop(item.name)
|
rm_supp_cost.pop(item.name)
|
||||||
|
|
||||||
if item.recalculate_rate:
|
item.rate = (
|
||||||
item.rate = (
|
flt(item.rm_cost_per_qty) + flt(item.service_cost_per_qty) + flt(item.additional_cost_per_qty)
|
||||||
flt(item.rm_cost_per_qty) + flt(item.service_cost_per_qty) + flt(item.additional_cost_per_qty)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
item.received_qty = item.qty + flt(item.rejected_qty)
|
item.received_qty = item.qty + flt(item.rejected_qty)
|
||||||
item.amount = item.qty * item.rate
|
item.amount = item.qty * item.rate
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
"rate_and_amount",
|
"rate_and_amount",
|
||||||
"rate",
|
"rate",
|
||||||
"amount",
|
"amount",
|
||||||
"recalculate_rate",
|
|
||||||
"column_break_19",
|
"column_break_19",
|
||||||
"rm_cost_per_qty",
|
"rm_cost_per_qty",
|
||||||
"service_cost_per_qty",
|
"service_cost_per_qty",
|
||||||
@@ -196,7 +195,6 @@
|
|||||||
"options": "currency",
|
"options": "currency",
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"read_only_depends_on": "eval: doc.recalculate_rate",
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -466,18 +464,12 @@
|
|||||||
"fieldname": "accounting_details_section",
|
"fieldname": "accounting_details_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Accounting Details"
|
"label": "Accounting Details"
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "1",
|
|
||||||
"fieldname": "recalculate_rate",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Recalculate Rate"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-06 18:44:45.599761",
|
"modified": "2023-09-03 17:04:21.214316",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Receipt Item",
|
"name": "Subcontracting Receipt Item",
|
||||||
|
|||||||
Reference in New Issue
Block a user