Merge branch 'develop' into develop

This commit is contained in:
Vishnu VS
2023-04-26 18:56:09 +05:30
committed by GitHub
33 changed files with 216 additions and 140 deletions

View File

@@ -297,7 +297,7 @@ def _make_test_records(verbose=None):
# fixed asset depreciation # fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None], ["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
["_Test Depreciations", "Expenses", 0, None, None], ["_Test Depreciations", "Expenses", 0, "Depreciation", None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None], ["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
# Receivable / Payable Account # Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable", None], ["_Test Receivable", "Current Assets", 0, "Receivable", None],

View File

@@ -56,7 +56,7 @@ class BankClearance(Document):
select select
"Payment Entry" as payment_document, name as payment_entry, "Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date, reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount, 0) as credit, if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
if(paid_from=%(account)s, 0, received_amount) as debit, if(paid_from=%(account)s, 0, received_amount) as debit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency

View File

@@ -281,10 +281,13 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
) )
elif payment_entry.payment_document == "Journal Entry": elif payment_entry.payment_document == "Journal Entry":
return frappe.db.get_value( return abs(
"Journal Entry Account", frappe.db.get_value(
{"parent": payment_entry.payment_entry, "account": gl_bank_account}, "Journal Entry Account",
"sum(credit_in_account_currency)", {"parent": payment_entry.payment_entry, "account": gl_bank_account},
"sum(debit_in_account_currency-credit_in_account_currency)",
)
or 0
) )
elif payment_entry.payment_document == "Expense Claim": elif payment_entry.payment_document == "Expense Claim":

View File

@@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
frappe.ui.form.on("Journal Entry", { frappe.ui.form.on("Journal Entry", {
setup: function(frm) { setup: function(frm) {
frm.add_fetch("bank_account", "account", "account"); frm.add_fetch("bank_account", "account", "account");
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger"]; frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset Depreciation Schedule'];
}, },
refresh: function(frm) { refresh: function(frm) {

View File

@@ -69,6 +69,7 @@ class JournalEntry(AccountsController):
self.validate_empty_accounts_table() self.validate_empty_accounts_table()
self.set_account_and_party_balance() self.set_account_and_party_balance()
self.validate_inter_company_accounts() self.validate_inter_company_accounts()
self.validate_depr_entry_voucher_type()
if self.docstatus == 0: if self.docstatus == 0:
self.apply_tax_withholding() self.apply_tax_withholding()
@@ -130,6 +131,13 @@ class JournalEntry(AccountsController):
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit: if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry")) frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
def validate_depr_entry_voucher_type(self):
if (
any(d.account_type == "Depreciation" for d in self.get("accounts"))
and self.voucher_type != "Depreciation Entry"
):
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
def validate_stock_accounts(self): def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
for account in stock_accounts: for account in stock_accounts:
@@ -233,25 +241,30 @@ class JournalEntry(AccountsController):
self.remove(d) self.remove(d)
def update_asset_value(self): def update_asset_value(self):
if self.voucher_type != "Depreciation Entry": if self.flags.planned_depr_entry or self.voucher_type != "Depreciation Entry":
return return
processed_assets = []
for d in self.get("accounts"): for d in self.get("accounts"):
if ( if (
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets d.reference_type == "Asset"
and d.reference_name
and d.account_type == "Depreciation"
and d.debit
): ):
processed_assets.append(d.reference_name)
asset = frappe.get_doc("Asset", d.reference_name) asset = frappe.get_doc("Asset", d.reference_name)
if asset.calculate_depreciation: if asset.calculate_depreciation:
continue fb_idx = 1
if self.finance_book:
depr_value = d.debit or d.credit for fb_row in asset.get("finance_books"):
if fb_row.finance_book == self.finance_book:
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value) fb_idx = fb_row.idx
break
fb_row = asset.get("finance_books")[fb_idx - 1]
fb_row.value_after_depreciation -= d.debit
fb_row.db_update()
else:
asset.db_set("value_after_depreciation", asset.value_after_depreciation - d.debit)
asset.set_status() asset.set_status()
@@ -316,42 +329,47 @@ class JournalEntry(AccountsController):
if self.voucher_type != "Depreciation Entry": if self.voucher_type != "Depreciation Entry":
return return
processed_assets = []
for d in self.get("accounts"): for d in self.get("accounts"):
if ( if (
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets d.reference_type == "Asset"
and d.reference_name
and d.account_type == "Depreciation"
and d.debit
): ):
processed_assets.append(d.reference_name)
asset = frappe.get_doc("Asset", d.reference_name) asset = frappe.get_doc("Asset", d.reference_name)
if asset.calculate_depreciation: if asset.calculate_depreciation:
je_found = False je_found = False
for row in asset.get("finance_books"): for fb_row in asset.get("finance_books"):
if je_found: if je_found:
break break
depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book) depr_schedule = get_depr_schedule(asset.name, "Active", fb_row.finance_book)
for s in depr_schedule or []: for s in depr_schedule or []:
if s.journal_entry == self.name: if s.journal_entry == self.name:
s.db_set("journal_entry", None) s.db_set("journal_entry", None)
row.value_after_depreciation += s.depreciation_amount fb_row.value_after_depreciation += d.debit
row.db_update() fb_row.db_update()
asset.set_status()
je_found = True je_found = True
break break
if not je_found:
fb_idx = 1
if self.finance_book:
for fb_row in asset.get("finance_books"):
if fb_row.finance_book == self.finance_book:
fb_idx = fb_row.idx
break
fb_row = asset.get("finance_books")[fb_idx - 1]
fb_row.value_after_depreciation += d.debit
fb_row.db_update()
else: else:
depr_value = d.debit or d.credit asset.db_set("value_after_depreciation", asset.value_after_depreciation + d.debit)
asset.set_status()
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
asset.set_status()
def unlink_inter_company_jv(self): def unlink_inter_company_jv(self):
if ( if (

View File

@@ -1693,7 +1693,10 @@ def get_payment_entry(
): ):
reference_doc = None reference_doc = None
doc = frappe.get_doc(dt, dn) doc = frappe.get_doc(dt, dn)
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= 99.99: over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
100.0 + over_billing_allowance
):
frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
if not party_type: if not party_type:

View File

@@ -215,7 +215,7 @@ def get_tax_row_for_tds(tax_details, tax_amount):
} }
def get_lower_deduction_certificate(tax_details, pan_no): def get_lower_deduction_certificate(company, tax_details, pan_no):
ldc_name = frappe.db.get_value( ldc_name = frappe.db.get_value(
"Lower Deduction Certificate", "Lower Deduction Certificate",
{ {
@@ -223,6 +223,7 @@ def get_lower_deduction_certificate(tax_details, pan_no):
"tax_withholding_category": tax_details.tax_withholding_category, "tax_withholding_category": tax_details.tax_withholding_category,
"valid_from": (">=", tax_details.from_date), "valid_from": (">=", tax_details.from_date),
"valid_upto": ("<=", tax_details.to_date), "valid_upto": ("<=", tax_details.to_date),
"company": company,
}, },
"name", "name",
) )
@@ -255,7 +256,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
tax_amount = 0 tax_amount = 0
if party_type == "Supplier": if party_type == "Supplier":
ldc = get_lower_deduction_certificate(tax_details, pan_no) ldc = get_lower_deduction_certificate(inv.company, tax_details, pan_no)
if tax_deducted: if tax_deducted:
net_total = inv.tax_withholding_net_total net_total = inv.tax_withholding_net_total
if ldc: if ldc:

View File

@@ -114,28 +114,6 @@ def get_assets(filters):
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category, from (SELECT a.asset_category,
ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
ds.depreciation_amount
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
ds.depreciation_amount
else
0
end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
ds.depreciation_amount
else
0
end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabAsset Depreciation Schedule` ads, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and ads.asset = a.name and ads.docstatus=1 and ads.name = ds.parent and ifnull(ds.journal_entry, '') != ''
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
gle.debit gle.debit
else else
@@ -160,7 +138,7 @@ def get_assets(filters):
aca.parent = a.asset_category and aca.company_name = %(company)s aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on join `tabCompany` company on
company.name = %(company)s company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
group by a.asset_category group by a.asset_category
union union
SELECT a.asset_category, SELECT a.asset_category,

View File

@@ -80,7 +80,7 @@ def get_entries(filters):
payment_entries = frappe.db.sql( payment_entries = frappe.db.sql(
"""SELECT """SELECT
"Payment Entry", name, posting_date, reference_no, clearance_date, party, "Payment Entry", name, posting_date, reference_no, clearance_date, party,
if(paid_from=%(account)s, paid_amount * -1, received_amount) if(paid_from=%(account)s, ((paid_amount * -1) - total_taxes_and_charges) , received_amount)
FROM FROM
`tabPayment Entry` `tabPayment Entry`
WHERE WHERE

View File

@@ -4,7 +4,6 @@
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt
def execute(filters=None): def execute(filters=None):
@@ -66,12 +65,6 @@ def get_result(
else: else:
total_amount_credited += entry.credit total_amount_credited += entry.credit
## Check if ldc is applied and show rate as per ldc
actual_rate = (tds_deducted / total_amount_credited) * 100
if flt(actual_rate) < flt(rate):
rate = actual_rate
if tds_deducted: if tds_deducted:
row = { row = {
"pan" "pan"

View File

@@ -1374,10 +1374,7 @@ def get_stock_and_account_balance(account=None, posting_date=None, company=None)
if wh_details.account == account and not wh_details.is_group if wh_details.account == account and not wh_details.is_group
] ]
total_stock_value = 0.0 total_stock_value = get_stock_value_on(related_warehouses, posting_date)
for warehouse in related_warehouses:
value = get_stock_value_on(warehouse, posting_date)
total_stock_value += value
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses

View File

@@ -783,7 +783,7 @@ def make_journal_entry(asset_name):
je.voucher_type = "Depreciation Entry" je.voucher_type = "Depreciation Entry"
je.naming_series = depreciation_series je.naming_series = depreciation_series
je.company = asset.company je.company = asset.company
je.remark = "Depreciation Entry against asset {0}".format(asset_name) je.remark = _("Depreciation Entry against asset {0}").format(asset_name)
je.append( je.append(
"accounts", "accounts",

View File

@@ -157,6 +157,7 @@ def make_depreciation_entry(asset_depr_schedule_name, date=None):
je.append("accounts", debit_entry) je.append("accounts", debit_entry)
je.flags.ignore_permissions = True je.flags.ignore_permissions = True
je.flags.planned_depr_entry = True
je.save() je.save()
if not je.meta.get_workflow(): if not je.meta.get_workflow():
je.submit() je.submit()

View File

@@ -1511,7 +1511,7 @@ class TestDepreciationBasics(AssetSetup):
) )
self.assertEqual(asset.status, "Submitted") self.assertEqual(asset.status, "Submitted")
self.assertEqual(asset.get("value_after_depreciation"), 100000) self.assertEqual(asset.get_value_after_depreciation(), 100000)
jv = make_journal_entry( jv = make_journal_entry(
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False "_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
@@ -1524,12 +1524,68 @@ class TestDepreciationBasics(AssetSetup):
jv.submit() jv.submit()
asset.reload() asset.reload()
self.assertEqual(asset.get("value_after_depreciation"), 99900) self.assertEqual(asset.get_value_after_depreciation(), 99900)
jv.cancel() jv.cancel()
asset.reload() asset.reload()
self.assertEqual(asset.get("value_after_depreciation"), 100000) self.assertEqual(asset.get_value_after_depreciation(), 100000)
def test_manual_depreciation_for_depreciable_asset(self):
asset = create_asset(
item_code="Macbook Pro",
calculate_depreciation=1,
purchase_date="2020-01-30",
available_for_use_date="2020-01-30",
expected_value_after_useful_life=10000,
total_number_of_depreciations=10,
frequency_of_depreciation=1,
submit=1,
)
self.assertEqual(asset.status, "Submitted")
self.assertEqual(asset.get_value_after_depreciation(), 100000)
jv = make_journal_entry(
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
)
for d in jv.accounts:
d.reference_type = "Asset"
d.reference_name = asset.name
jv.voucher_type = "Depreciation Entry"
jv.insert()
jv.submit()
asset.reload()
self.assertEqual(asset.get_value_after_depreciation(), 99900)
jv.cancel()
asset.reload()
self.assertEqual(asset.get_value_after_depreciation(), 100000)
def test_manual_depreciation_with_incorrect_jv_voucher_type(self):
asset = create_asset(
item_code="Macbook Pro",
calculate_depreciation=1,
purchase_date="2020-01-30",
available_for_use_date="2020-01-30",
expected_value_after_useful_life=10000,
total_number_of_depreciations=10,
frequency_of_depreciation=1,
submit=1,
)
jv = make_journal_entry(
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
)
for d in jv.accounts:
d.reference_type = "Asset"
d.reference_name = asset.name
d.account_type = "Depreciation"
jv.voucher_type = "Journal Entry"
self.assertRaises(frappe.ValidationError, jv.insert)
def create_asset_data(): def create_asset_data():

View File

@@ -94,11 +94,11 @@ frappe.query_reports["Fixed Asset Register"] = {
label: __("Finance Book"), label: __("Finance Book"),
fieldtype: "Link", fieldtype: "Link",
options: "Finance Book", options: "Finance Book",
depends_on: "eval: doc.only_depreciable_assets == 1", depends_on: "eval: doc.filter_by_finance_book == 1",
}, },
{ {
fieldname:"only_depreciable_assets", fieldname:"filter_by_finance_book",
label: __("Only depreciable assets"), label: __("Filter by Finance Book"),
fieldtype: "Check" fieldtype: "Check"
}, },
{ {

View File

@@ -45,8 +45,6 @@ def get_conditions(filters):
filters.year_end_date = getdate(fiscal_year.year_end_date) filters.year_end_date = getdate(fiscal_year.year_end_date)
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]] conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
if filters.get("only_depreciable_assets"):
conditions["calculate_depreciation"] = filters.get("only_depreciable_assets")
if filters.get("only_existing_assets"): if filters.get("only_existing_assets"):
conditions["is_existing_asset"] = filters.get("only_existing_assets") conditions["is_existing_asset"] = filters.get("only_existing_assets")
if filters.get("asset_category"): if filters.get("asset_category"):
@@ -106,7 +104,7 @@ def get_data(filters):
assets_linked_to_fb = None assets_linked_to_fb = None
if filters.only_depreciable_assets: if filters.filter_by_finance_book:
assets_linked_to_fb = frappe.db.get_all( assets_linked_to_fb = frappe.db.get_all(
doctype="Asset Finance Book", doctype="Asset Finance Book",
filters={"finance_book": filters.finance_book or ("is", "not set")}, filters={"finance_book": filters.finance_book or ("is", "not set")},

View File

@@ -43,7 +43,7 @@ frappe.listview_settings['Purchase Order'] = {
}); });
listview.page.add_action_item(__("Advance Payment"), ()=>{ listview.page.add_action_item(__("Advance Payment"), ()=>{
erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Advance Payment"); erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Payment Entry");
}); });
} }

View File

@@ -1662,7 +1662,10 @@ class AccountsController(TransactionBase):
) )
self.append("payment_schedule", data) self.append("payment_schedule", data)
if not automatically_fetch_payment_terms: if not (
automatically_fetch_payment_terms
and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
):
for d in self.get("payment_schedule"): for d in self.get("payment_schedule"):
if d.invoice_portion: if d.invoice_portion:
d.payment_amount = flt( d.payment_amount = flt(
@@ -1901,12 +1904,14 @@ class AccountsController(TransactionBase):
reconcilation_entry.party = secondary_party reconcilation_entry.party = secondary_party
reconcilation_entry.reference_type = self.doctype reconcilation_entry.reference_type = self.doctype
reconcilation_entry.reference_name = self.name reconcilation_entry.reference_name = self.name
reconcilation_entry.cost_center = self.cost_center reconcilation_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(
self.company
)
advance_entry.account = primary_account advance_entry.account = primary_account
advance_entry.party_type = primary_party_type advance_entry.party_type = primary_party_type
advance_entry.party = primary_party advance_entry.party = primary_party
advance_entry.cost_center = self.cost_center advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company)
advance_entry.is_advance = "Yes" advance_entry.is_advance = "Yes"
if self.doctype == "Sales Invoice": if self.doctype == "Sales Invoice":

View File

@@ -576,7 +576,9 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist() @frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs @frappe.validate_and_sanitize_search_inputs
def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters): def get_filtered_dimensions(
doctype, txt, searchfield, start, page_len, filters, reference_doctype=None
):
from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import ( from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
get_dimension_filter_map, get_dimension_filter_map,
) )
@@ -617,7 +619,12 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters)
query_filters.append(["name", query_selector, dimensions]) query_filters.append(["name", query_selector, dimensions])
output = frappe.get_list( output = frappe.get_list(
doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1 doctype,
fields=fields,
filters=query_filters,
or_filters=or_filters,
as_list=1,
reference_doctype=reference_doctype,
) )
return [tuple(d) for d in set(output)] return [tuple(d) for d in set(output)]

View File

@@ -59,7 +59,7 @@ class Opportunity(TransactionBase, CRMNote):
if not self.get(field) and frappe.db.field_exists(self.opportunity_from, field): if not self.get(field) and frappe.db.field_exists(self.opportunity_from, field):
try: try:
value = frappe.db.get_value(self.opportunity_from, self.party_name, field) value = frappe.db.get_value(self.opportunity_from, self.party_name, field)
self.db_set(field, value) self.set(field, value)
except Exception: except Exception:
continue continue

View File

@@ -652,7 +652,7 @@ frappe.ui.form.on("BOM Operation", "operation", function(frm, cdt, cdn) {
frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) { frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
if(!d.workstation) return;
frappe.call({ frappe.call({
"method": "frappe.client.get", "method": "frappe.client.get",
args: { args: {

View File

@@ -50,7 +50,7 @@ frappe.ui.form.on('BOM Operation', {
workstation: function(frm, cdt, cdn) { workstation: function(frm, cdt, cdn) {
const d = locals[cdt][cdn]; const d = locals[cdt][cdn];
if(!d.workstation) return;
frappe.call({ frappe.call({
"method": "frappe.client.get", "method": "frappe.client.get",
args: { args: {

View File

@@ -332,3 +332,4 @@ execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_h
erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.patches.v14_0.migrate_gl_to_payment_ledger
execute:frappe.delete_doc_if_exists("Report", "Tax Detail") execute:frappe.delete_doc_if_exists("Report", "Tax Detail")
erpnext.patches.v15_0.enable_all_leads erpnext.patches.v15_0.enable_all_leads
erpnext.patches.v14_0.update_company_in_ldc

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import frappe
from erpnext import get_default_company
def execute():
company = get_default_company()
if company:
for d in frappe.get_all("Lower Deduction Certificate", pluck="name"):
frappe.db.set_value("Lower Deduction Certificate", d, "company", company, update_modified=False)

View File

@@ -68,7 +68,7 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) {
// New activity if no activities found // New activity if no activities found
var args = dialog.get_values(); var args = dialog.get_values();
if(!args) return; if(!args) return;
if (frm.doc.time_logs.length <= 1 && !frm.doc.time_logs[0].activity_type && !frm.doc.time_logs[0].from_time) { if (frm.doc.time_logs.length == 1 && !frm.doc.time_logs[0].activity_type && !frm.doc.time_logs[0].from_time) {
frm.doc.time_logs = []; frm.doc.time_logs = [];
} }
row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs"); row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs");

View File

@@ -10,6 +10,7 @@
"tax_withholding_category", "tax_withholding_category",
"fiscal_year", "fiscal_year",
"column_break_3", "column_break_3",
"company",
"certificate_no", "certificate_no",
"section_break_3", "section_break_3",
"supplier", "supplier",
@@ -123,11 +124,18 @@
"label": "Tax Withholding Category", "label": "Tax Withholding Category",
"options": "Tax Withholding Category", "options": "Tax Withholding Category",
"reqd": 1 "reqd": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2021-10-23 18:33:38.962622", "modified": "2023-04-18 08:25:35.302081",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Regional", "module": "Regional",
"name": "Lower Deduction Certificate", "name": "Lower Deduction Certificate",
@@ -136,5 +144,6 @@
"permissions": [], "permissions": [],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -1340,8 +1340,9 @@ def get_work_order_items(sales_order, for_raw_material_request=0):
.select(Sum(wo.qty)) .select(Sum(wo.qty))
.where( .where(
(wo.production_item == i.item_code) (wo.production_item == i.item_code)
& (wo.sales_order == so.name) * (wo.sales_order_item == i.name) & (wo.sales_order == so.name)
& (wo.docstatus.lte(2)) & (wo.sales_order_item == i.name)
& (wo.docstatus.lt(2))
) )
.run()[0][0] .run()[0][0]
) )

View File

@@ -57,7 +57,7 @@ frappe.listview_settings['Sales Order'] = {
}); });
listview.page.add_action_item(__("Advance Payment"), ()=>{ listview.page.add_action_item(__("Advance Payment"), ()=>{
erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Advance Payment"); erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Payment Entry");
}); });
} }

View File

@@ -271,6 +271,9 @@ class DeliveryNote(SellingController):
def check_credit_limit(self): def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.selling.doctype.customer.customer import check_credit_limit
if self.per_billed == 100:
return
extra_amount = 0 extra_amount = 0
validate_against_credit_limit = False validate_against_credit_limit = False
bypass_credit_limit_check_at_sales_order = cint( bypass_credit_limit_check_at_sales_order = cint(

View File

@@ -84,7 +84,7 @@ def get_data(report_filters):
closing_date = add_days(from_date, -1) closing_date = add_days(from_date, -1)
for key, stock_data in voucher_wise_dict.items(): for key, stock_data in voucher_wise_dict.items():
prev_stock_value = get_stock_value_on( prev_stock_value = get_stock_value_on(
posting_date=closing_date, item_code=key[0], warehouse=key[1] posting_date=closing_date, item_code=key[0], warehouses=key[1]
) )
for data in stock_data: for data in stock_data:
expected_stock_value = prev_stock_value + data.stock_value_difference expected_stock_value = prev_stock_value + data.stock_value_difference

View File

@@ -41,7 +41,7 @@ def get_data(report_filters):
key = (d.voucher_type, d.voucher_no) key = (d.voucher_type, d.voucher_no)
gl_data = voucher_wise_gl_data.get(key) or {} gl_data = voucher_wise_gl_data.get(key) or {}
d.account_value = gl_data.get("account_value", 0) d.account_value = gl_data.get("account_value", 0)
d.difference_value = abs(d.stock_value) - abs(d.account_value) d.difference_value = d.stock_value - d.account_value
if abs(d.difference_value) > 0.1: if abs(d.difference_value) > 0.1:
data.append(d) data.append(d)

View File

@@ -7,10 +7,11 @@ from typing import Dict, Optional
import frappe import frappe
from frappe import _ from frappe import _
from frappe.query_builder.functions import CombineDatetime from frappe.query_builder.functions import CombineDatetime, IfNull, Sum
from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
import erpnext import erpnext
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.valuation import FIFOValuation, LIFOValuation from erpnext.stock.valuation import FIFOValuation, LIFOValuation
BarcodeScanResult = Dict[str, Optional[str]] BarcodeScanResult = Dict[str, Optional[str]]
@@ -53,50 +54,36 @@ def get_stock_value_from_bin(warehouse=None, item_code=None):
return stock_value return stock_value
def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): def get_stock_value_on(
warehouses: list | str = None, posting_date: str = None, item_code: str = None
) -> float:
if not posting_date: if not posting_date:
posting_date = nowdate() posting_date = nowdate()
values, condition = [posting_date], "" sle = frappe.qb.DocType("Stock Ledger Entry")
query = (
if warehouse: frappe.qb.from_(sle)
.select(IfNull(Sum(sle.stock_value_difference), 0))
lft, rgt, is_group = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt", "is_group"]) .where((sle.posting_date <= posting_date) & (sle.is_cancelled == 0))
.orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=frappe.qb.desc)
if is_group: .orderby(sle.creation, order=frappe.qb.desc)
values.extend([lft, rgt])
condition += "and exists (\
select name from `tabWarehouse` wh where wh.name = sle.warehouse\
and wh.lft >= %s and wh.rgt <= %s)"
else:
values.append(warehouse)
condition += " AND warehouse = %s"
if item_code:
values.append(item_code)
condition += " AND item_code = %s"
stock_ledger_entries = frappe.db.sql(
"""
SELECT item_code, stock_value, name, warehouse
FROM `tabStock Ledger Entry` sle
WHERE posting_date <= %s {0}
and is_cancelled = 0
ORDER BY timestamp(posting_date, posting_time) DESC, creation DESC
""".format(
condition
),
values,
as_dict=1,
) )
sle_map = {} if warehouses:
for sle in stock_ledger_entries: if isinstance(warehouses, str):
if not (sle.item_code, sle.warehouse) in sle_map: warehouses = [warehouses]
sle_map[(sle.item_code, sle.warehouse)] = flt(sle.stock_value)
return sum(sle_map.values()) warehouses = set(warehouses)
for wh in list(warehouses):
if frappe.db.get_value("Warehouse", wh, "is_group"):
warehouses.update(get_child_warehouses(wh))
query = query.where(sle.warehouse.isin(warehouses))
if item_code:
query = query.where(sle.item_code == item_code)
return query.run(as_list=True)[0][0]
@frappe.whitelist() @frappe.whitelist()

View File

@@ -69,7 +69,7 @@ def task(doc_name, from_doctype, to_doctype):
"Sales Order": { "Sales Order": {
"Sales Invoice": sales_order.make_sales_invoice, "Sales Invoice": sales_order.make_sales_invoice,
"Delivery Note": sales_order.make_delivery_note, "Delivery Note": sales_order.make_delivery_note,
"Advance Payment": payment_entry.get_payment_entry, "Payment Entry": payment_entry.get_payment_entry,
}, },
"Sales Invoice": { "Sales Invoice": {
"Delivery Note": sales_invoice.make_delivery_note, "Delivery Note": sales_invoice.make_delivery_note,
@@ -86,11 +86,11 @@ def task(doc_name, from_doctype, to_doctype):
"Supplier Quotation": { "Supplier Quotation": {
"Purchase Order": supplier_quotation.make_purchase_order, "Purchase Order": supplier_quotation.make_purchase_order,
"Purchase Invoice": supplier_quotation.make_purchase_invoice, "Purchase Invoice": supplier_quotation.make_purchase_invoice,
"Advance Payment": payment_entry.get_payment_entry,
}, },
"Purchase Order": { "Purchase Order": {
"Purchase Invoice": purchase_order.make_purchase_invoice, "Purchase Invoice": purchase_order.make_purchase_invoice,
"Purchase Receipt": purchase_order.make_purchase_receipt, "Purchase Receipt": purchase_order.make_purchase_receipt,
"Payment Entry": payment_entry.get_payment_entry,
}, },
"Purchase Invoice": { "Purchase Invoice": {
"Purchase Receipt": purchase_invoice.make_purchase_receipt, "Purchase Receipt": purchase_invoice.make_purchase_receipt,
@@ -98,12 +98,13 @@ def task(doc_name, from_doctype, to_doctype):
}, },
"Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice}, "Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice},
} }
if to_doctype in ["Advance Payment", "Payment Entry"]: if to_doctype in ["Payment Entry"]:
obj = mapper[from_doctype][to_doctype](from_doctype, doc_name) obj = mapper[from_doctype][to_doctype](from_doctype, doc_name)
else: else:
obj = mapper[from_doctype][to_doctype](doc_name) obj = mapper[from_doctype][to_doctype](doc_name)
obj.flags.ignore_validate = True obj.flags.ignore_validate = True
obj.set_title_field()
obj.insert(ignore_mandatory=True) obj.insert(ignore_mandatory=True)