Merge branch 'develop' of https://github.com/frappe/erpnext into pending_tds_vouchers

This commit is contained in:
Deepesh Garg
2022-09-16 12:35:39 +05:30
13 changed files with 84 additions and 23 deletions

View File

@@ -37,7 +37,7 @@ class Account(NestedSet):
def autoname(self): def autoname(self):
from erpnext.accounts.utils import get_autoname_with_number from erpnext.accounts.utils import get_autoname_with_number
self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company) self.name = get_autoname_with_number(self.account_number, self.account_name, self.company)
def validate(self): def validate(self):
from erpnext.accounts.utils import validate_field_number from erpnext.accounts.utils import validate_field_number

View File

@@ -16,7 +16,7 @@ class CostCenter(NestedSet):
from erpnext.accounts.utils import get_autoname_with_number from erpnext.accounts.utils import get_autoname_with_number
self.name = get_autoname_with_number( self.name = get_autoname_with_number(
self.cost_center_number, self.cost_center_name, None, self.company self.cost_center_number, self.cost_center_name, self.company
) )
def validate(self): def validate(self):

View File

@@ -2429,7 +2429,6 @@ def create_dunning(source_name, target_doc=None):
target.closing_text = letter_text.get("closing_text") target.closing_text = letter_text.get("closing_text")
target.language = letter_text.get("language") target.language = letter_text.get("language")
amounts = calculate_interest_and_amount( amounts = calculate_interest_and_amount(
target.posting_date,
target.outstanding_amount, target.outstanding_amount,
target.rate_of_interest, target.rate_of_interest,
target.dunning_fee, target.dunning_fee,

View File

@@ -22,8 +22,7 @@ def get_columns():
{ {
"label": _("Payment Document Type"), "label": _("Payment Document Type"),
"fieldname": "payment_document_type", "fieldname": "payment_document_type",
"fieldtype": "Link", "fieldtype": "Data",
"options": "Doctype",
"width": 130, "width": 130,
}, },
{ {
@@ -33,15 +32,15 @@ def get_columns():
"options": "payment_document_type", "options": "payment_document_type",
"width": 140, "width": 140,
}, },
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100}, {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
{"label": _("Cheque/Reference No"), "fieldname": "cheque_no", "width": 120}, {"label": _("Cheque/Reference No"), "fieldname": "cheque_no", "width": 120},
{"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 100}, {"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 120},
{ {
"label": _("Against Account"), "label": _("Against Account"),
"fieldname": "against", "fieldname": "against",
"fieldtype": "Link", "fieldtype": "Link",
"options": "Account", "options": "Account",
"width": 170, "width": 200,
}, },
{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120}, {"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
] ]

View File

@@ -1037,7 +1037,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company, m
frappe.db.set_value("Cost Center", docname, "cost_center_name", cost_center_name.strip()) frappe.db.set_value("Cost Center", docname, "cost_center_name", cost_center_name.strip())
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company) new_name = get_autoname_with_number(cost_center_number, cost_center_name, company)
if docname != new_name: if docname != new_name:
frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge) frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge)
return new_name return new_name
@@ -1060,16 +1060,14 @@ def validate_field_number(doctype_name, docname, number_value, company, field_na
) )
def get_autoname_with_number(number_value, doc_title, name, company): def get_autoname_with_number(number_value, doc_title, company):
"""append title with prefix as number and suffix as company's abbreviation separated by '-'""" """append title with prefix as number and suffix as company's abbreviation separated by '-'"""
if name: company_abbr = frappe.get_cached_value("Company", company, "abbr")
name_split = name.split("-") parts = [doc_title.strip(), company_abbr]
parts = [doc_title.strip(), name_split[len(name_split) - 1].strip()]
else:
abbr = frappe.get_cached_value("Company", company, ["abbr"], as_dict=True)
parts = [doc_title.strip(), abbr.abbr]
if cstr(number_value).strip(): if cstr(number_value).strip():
parts.insert(0, cstr(number_value).strip()) parts.insert(0, cstr(number_value).strip())
return " - ".join(parts) return " - ".join(parts)

View File

@@ -129,6 +129,18 @@ class TestAssetRepair(unittest.TestCase):
def test_gl_entries_with_perpetual_inventory(self): def test_gl_entries_with_perpetual_inventory(self):
set_depreciation_settings_in_company(company="_Test Company with perpetual inventory") set_depreciation_settings_in_company(company="_Test Company with perpetual inventory")
asset_category = frappe.get_doc("Asset Category", "Computers")
asset_category.append(
"accounts",
{
"company_name": "_Test Company with perpetual inventory",
"fixed_asset_account": "_Test Fixed Asset - TCP1",
"accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1",
"depreciation_expense_account": "_Test Depreciations - TCP1",
},
)
asset_category.save()
asset_repair = create_asset_repair( asset_repair = create_asset_repair(
capitalize_repair_cost=1, capitalize_repair_cost=1,
stock_consumption=1, stock_consumption=1,

View File

@@ -6,6 +6,7 @@ import frappe
from frappe import ValidationError, _, msgprint from frappe import ValidationError, _, msgprint
from frappe.contacts.doctype.address.address import get_address_display from frappe.contacts.doctype.address.address import get_address_display
from frappe.utils import cint, cstr, flt, getdate from frappe.utils import cint, cstr, flt, getdate
from frappe.utils.data import nowtime
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
from erpnext.accounts.party import get_party_details from erpnext.accounts.party import get_party_details
@@ -289,12 +290,16 @@ class BuyingController(SubcontractingController):
# Get outgoing rate based on original item cost based on valuation method # Get outgoing rate based on original item cost based on valuation method
if not d.get(frappe.scrub(ref_doctype)): if not d.get(frappe.scrub(ref_doctype)):
posting_time = self.get("posting_time")
if not posting_time and self.doctype == "Purchase Order":
posting_time = nowtime()
outgoing_rate = get_incoming_rate( outgoing_rate = get_incoming_rate(
{ {
"item_code": d.item_code, "item_code": d.item_code,
"warehouse": d.get("from_warehouse"), "warehouse": d.get("from_warehouse"),
"posting_date": self.get("posting_date") or self.get("transation_date"), "posting_date": self.get("posting_date") or self.get("transation_date"),
"posting_time": self.get("posting_time"), "posting_time": posting_time,
"qty": -1 * flt(d.get("stock_qty")), "qty": -1 * flt(d.get("stock_qty")),
"serial_no": d.get("serial_no"), "serial_no": d.get("serial_no"),
"batch_no": d.get("batch_no"), "batch_no": d.get("batch_no"),

View File

@@ -519,6 +519,8 @@ def get_accrued_interest_entries(against_loan, posting_date=None):
if not posting_date: if not posting_date:
posting_date = getdate() posting_date = getdate()
precision = cint(frappe.db.get_default("currency_precision")) or 2
unpaid_accrued_entries = frappe.db.sql( unpaid_accrued_entries = frappe.db.sql(
""" """
SELECT name, posting_date, interest_amount - paid_interest_amount as interest_amount, SELECT name, posting_date, interest_amount - paid_interest_amount as interest_amount,
@@ -539,6 +541,13 @@ def get_accrued_interest_entries(against_loan, posting_date=None):
as_dict=1, as_dict=1,
) )
# Skip entries with zero interest amount & payable principal amount
unpaid_accrued_entries = [
d
for d in unpaid_accrued_entries
if flt(d.interest_amount, precision) > 0 or flt(d.payable_principal_amount, precision) > 0
]
return unpaid_accrued_entries return unpaid_accrued_entries

View File

@@ -12,7 +12,7 @@ def execute():
frappe.qb.from_(dt) frappe.qb.from_(dt)
.select(dt.name, dt.notes, dt.modified_by, dt.modified) .select(dt.name, dt.notes, dt.modified_by, dt.modified)
.where(dt.notes.isnotnull() & dt.notes != "") .where(dt.notes.isnotnull() & dt.notes != "")
).run() ).run(as_dict=True)
for d in records: for d in records:
if strip_html(cstr(d.notes)).strip(): if strip_html(cstr(d.notes)).strip():

View File

@@ -389,6 +389,7 @@ class Company(NestedSet):
"capital_work_in_progress_account": "Capital Work in Progress", "capital_work_in_progress_account": "Capital Work in Progress",
"asset_received_but_not_billed": "Asset Received But Not Billed", "asset_received_but_not_billed": "Asset Received But Not Billed",
"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation", "expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation",
"default_expense_account": "Cost of Goods Sold",
} }
if self.enable_perpetual_inventory: if self.enable_perpetual_inventory:
@@ -398,7 +399,6 @@ class Company(NestedSet):
"default_inventory_account": "Stock", "default_inventory_account": "Stock",
"stock_adjustment_account": "Stock Adjustment", "stock_adjustment_account": "Stock Adjustment",
"expenses_included_in_valuation": "Expenses Included In Valuation", "expenses_included_in_valuation": "Expenses Included In Valuation",
"default_expense_account": "Cost of Goods Sold",
} }
) )

View File

@@ -945,7 +945,12 @@ class Item(Document):
if doctype == "Product Bundle": if doctype == "Product Bundle":
filters = {"new_item_code": self.name} filters = {"new_item_code": self.name}
if doctype in ( if linked_doc := frappe.db.get_value(
doctype, filters, ["new_item_code as docname"], as_dict=True
):
return linked_doc.update({"doctype": doctype})
elif doctype in (
"Purchase Invoice Item", "Purchase Invoice Item",
"Sales Invoice Item", "Sales Invoice Item",
): ):

View File

@@ -786,6 +786,36 @@ class TestItem(FrappeTestCase):
item.save() item.save()
self.assertTrue(len(item.customer_code) > 140) self.assertTrue(len(item.customer_code) > 140)
def test_update_is_stock_item(self):
# Step - 1: Create an Item with Maintain Stock enabled
item = make_item(properties={"is_stock_item": 1})
# Step - 2: Disable Maintain Stock
item.is_stock_item = 0
item.save()
item.reload()
self.assertEqual(item.is_stock_item, 0)
# Step - 3: Create Product Bundle
pb = frappe.new_doc("Product Bundle")
pb.new_item_code = item.name
pb.flags.ignore_mandatory = True
pb.save()
# Step - 4: Try to enable Maintain Stock, should throw a validation error
item.is_stock_item = 1
self.assertRaises(frappe.ValidationError, item.save)
item.reload()
# Step - 5: Delete Product Bundle
pb.delete()
# Step - 6: Again try to enable Maintain Stock
item.is_stock_item = 1
item.save()
item.reload()
self.assertEqual(item.is_stock_item, 1)
def set_item_variant_settings(fields): def set_item_variant_settings(fields):
doc = frappe.get_doc("Item Variant Settings") doc = frappe.get_doc("Item Variant Settings")

View File

@@ -729,15 +729,19 @@ def create_stock_reconciliation(**args):
sr.expense_account = args.expense_account or ( sr.expense_account = args.expense_account or (
( (
frappe.get_cached_value("Company", sr.company, "stock_adjustment_account") frappe.get_cached_value("Company", sr.company, "stock_adjustment_account")
or "Stock Adjustment - _TC" or frappe.get_cached_value(
"Account", {"account_type": "Stock Adjustment", "company": sr.company}, "name"
)
) )
if frappe.get_all("Stock Ledger Entry", {"company": sr.company}) if frappe.get_all("Stock Ledger Entry", {"company": sr.company})
else "Temporary Opening - _TC" else frappe.get_cached_value(
"Account", {"account_type": "Temporary", "company": sr.company}, "name"
)
) )
sr.cost_center = ( sr.cost_center = (
args.cost_center args.cost_center
or frappe.get_cached_value("Company", sr.company, "cost_center") or frappe.get_cached_value("Company", sr.company, "cost_center")
or "_Test Cost Center - _TC" or frappe.get_cached_value("Cost Center", filters={"is_group": 0, "company": sr.company})
) )
sr.append( sr.append(