mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-09 23:39:28 +00:00
Compare commits
3 Commits
v15.40.0
...
mergify/bp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2530b56421 | ||
|
|
3fd89d75d6 | ||
|
|
63e9da92b2 |
@@ -4,7 +4,7 @@ import inspect
|
||||
import frappe
|
||||
from frappe.utils.user import is_website_user
|
||||
|
||||
__version__ = "15.40.0"
|
||||
__version__ = "15.28.2"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -58,7 +58,7 @@ frappe.ui.form.on("Accounting Dimension", {
|
||||
},
|
||||
|
||||
label: function (frm) {
|
||||
frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase());
|
||||
frm.set_value("fieldname", frappe.model.scrub(frm.doc.label));
|
||||
},
|
||||
|
||||
document_type: function (frm) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import json
|
||||
import frappe
|
||||
from frappe import _, scrub
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||
from frappe.database.schema import validate_column_name
|
||||
from frappe.model import core_doctypes_list
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr
|
||||
@@ -61,7 +60,6 @@ class AccountingDimension(Document):
|
||||
if not self.is_new():
|
||||
self.validate_document_type_change()
|
||||
|
||||
validate_column_name(self.fieldname)
|
||||
self.validate_dimension_defaults()
|
||||
|
||||
def validate_document_type_change(self):
|
||||
|
||||
@@ -323,7 +323,6 @@ class PaymentReconciliation(Document):
|
||||
"posting_date": inv.posting_date,
|
||||
"currency": inv.currency,
|
||||
"cost_center": inv.cost_center,
|
||||
"remarks": inv.remarks,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"amount",
|
||||
"difference_amount",
|
||||
"sec_break1",
|
||||
"remarks",
|
||||
"remark",
|
||||
"currency",
|
||||
"exchange_rate",
|
||||
"cost_center"
|
||||
@@ -74,6 +74,12 @@
|
||||
"fieldname": "sec_break1",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "remark",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Remark",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@@ -99,18 +105,12 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Remarks",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_virtual": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-29 16:24:43.021230",
|
||||
"modified": "2023-11-17 17:33:34.818530",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Payment",
|
||||
|
||||
@@ -27,7 +27,7 @@ class PaymentReconciliationPayment(Document):
|
||||
reference_name: DF.DynamicLink | None
|
||||
reference_row: DF.Data | None
|
||||
reference_type: DF.Link | None
|
||||
remarks: DF.SmallText | None
|
||||
remark: DF.SmallText | None
|
||||
# end: auto-generated types
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"party_details",
|
||||
"party_type",
|
||||
"party",
|
||||
"party_name",
|
||||
"column_break_4",
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
@@ -423,19 +422,13 @@
|
||||
"label": "Party Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "party_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Party Name",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-23 12:23:40.117336",
|
||||
"modified": "2024-09-16 17:50:54.440090",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Request",
|
||||
@@ -474,4 +467,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ class PaymentRequest(Document):
|
||||
outstanding_amount: DF.Currency
|
||||
party: DF.DynamicLink | None
|
||||
party_account_currency: DF.Link | None
|
||||
party_name: DF.Data | None
|
||||
party_type: DF.Link | None
|
||||
payment_account: DF.ReadOnly | None
|
||||
payment_channel: DF.Literal["", "Email", "Phone", "Other"]
|
||||
@@ -610,7 +609,6 @@ def make_payment_request(**args):
|
||||
"party_type": party_type,
|
||||
"party": args.get("party") or ref_doc.get("customer"),
|
||||
"bank_account": bank_account,
|
||||
"party_name": args.get("party_name") or ref_doc.get("customer_name"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -354,8 +354,7 @@ class PeriodClosingVoucher(AccountsController):
|
||||
def get_account_closing_balances(self):
|
||||
pl_closing_entries = self.get_closing_entries_for_pl_accounts()
|
||||
bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts()
|
||||
closing_entries_for_closing_account = self.get_closing_entries_for_closing_account()
|
||||
closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account
|
||||
closing_entries = pl_closing_entries + bs_closing_entries
|
||||
return closing_entries
|
||||
|
||||
def get_closing_entries_for_pl_accounts(self):
|
||||
@@ -405,24 +404,12 @@ class PeriodClosingVoucher(AccountsController):
|
||||
self.update_default_dimensions(closing_entry, dimensions)
|
||||
return closing_entry
|
||||
|
||||
def get_closing_entries_for_closing_account(self):
|
||||
closing_entries = copy.deepcopy(self.closing_account_gle)
|
||||
for d in closing_entries:
|
||||
d.period_closing_voucher = self.name
|
||||
|
||||
return closing_entries
|
||||
|
||||
def is_first_period_closing_voucher(self):
|
||||
first_pcv = frappe.db.get_value(
|
||||
return not frappe.db.exists(
|
||||
"Period Closing Voucher",
|
||||
{"company": self.company, "docstatus": 1},
|
||||
"name",
|
||||
order_by="period_end_date",
|
||||
{"company": self.company, "docstatus": 1, "name": ("!=", self.name)},
|
||||
)
|
||||
|
||||
if not first_pcv or first_pcv == self.name:
|
||||
return True
|
||||
|
||||
def cancel_gl_entries(self):
|
||||
if self.get_gle_count_against_current_pcv() > 5000:
|
||||
frappe.enqueue(
|
||||
@@ -455,7 +442,8 @@ def process_gl_and_closing_entries(doc):
|
||||
make_gl_entries(gl_entries, merge_entries=False)
|
||||
|
||||
closing_entries = doc.get_account_closing_balances()
|
||||
make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date)
|
||||
if closing_entries:
|
||||
make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date)
|
||||
|
||||
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed")
|
||||
except Exception as e:
|
||||
|
||||
@@ -1593,11 +1593,7 @@ class PurchaseInvoice(BuyingController):
|
||||
for proj, value in projects.items():
|
||||
res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
|
||||
current_purchase_cost = res and res[0][0] or 0
|
||||
# frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
|
||||
project_doc = frappe.get_doc("Project", proj)
|
||||
project_doc.total_purchase_cost = current_purchase_cost + value
|
||||
project_doc.calculate_gross_margin()
|
||||
project_doc.db_update()
|
||||
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
|
||||
|
||||
def validate_supplier_invoice(self):
|
||||
if self.bill_date:
|
||||
|
||||
@@ -505,8 +505,7 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project",
|
||||
"print_hide": 1,
|
||||
"search_index": 1
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
@@ -975,7 +974,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-28 15:06:19.246141",
|
||||
"modified": "2024-07-19 12:12:42.449298",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
||||
@@ -1734,11 +1734,9 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
|
||||
def update_project(self):
|
||||
unique_projects = list(set([d.project for d in self.get("items") if d.project]))
|
||||
for p in unique_projects:
|
||||
project = frappe.get_doc("Project", p)
|
||||
if self.project:
|
||||
project = frappe.get_doc("Project", self.project)
|
||||
project.update_billed_amount()
|
||||
project.calculate_gross_margin()
|
||||
project.db_update()
|
||||
|
||||
def verify_payment_amount_is_positive(self):
|
||||
|
||||
@@ -812,8 +812,7 @@
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project",
|
||||
"search_index": 1
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.update_stock == 1",
|
||||
@@ -928,7 +927,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-28 15:06:40.980995",
|
||||
"modified": "2024-05-23 16:36:18.970862",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -514,7 +514,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
|
||||
payment_entry_filters.pop("apply_tax_withholding_amount", None)
|
||||
payment_entry_filters.pop("tax_withholding_category", None)
|
||||
|
||||
supp_inv_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
|
||||
supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
|
||||
|
||||
supp_jv_credit_amt = (
|
||||
frappe.db.get_value(
|
||||
@@ -538,7 +538,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
|
||||
group_by="payment_type",
|
||||
)
|
||||
|
||||
supp_credit_amt = supp_jv_credit_amt
|
||||
supp_credit_amt += supp_jv_credit_amt
|
||||
supp_credit_amt += inv.tax_withholding_net_total
|
||||
|
||||
for type in payment_entry_amounts:
|
||||
@@ -556,18 +556,18 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
|
||||
tax_withholding_net_total = inv.tax_withholding_net_total
|
||||
|
||||
if (threshold and tax_withholding_net_total >= threshold) or (
|
||||
cumulative_threshold and (supp_credit_amt + supp_inv_credit_amt) >= cumulative_threshold
|
||||
cumulative_threshold and supp_credit_amt >= cumulative_threshold
|
||||
):
|
||||
# Get net total again as TDS is calculated on net total
|
||||
# Grand is used to just check for threshold breach
|
||||
net_total = (
|
||||
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") or 0.0
|
||||
)
|
||||
supp_credit_amt += net_total
|
||||
|
||||
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
|
||||
tax_details.tax_on_excess_amount
|
||||
):
|
||||
# Get net total again as TDS is calculated on net total
|
||||
# Grand is used to just check for threshold breach
|
||||
net_total = (
|
||||
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
|
||||
or 0.0
|
||||
)
|
||||
net_total += inv.tax_withholding_net_total
|
||||
supp_credit_amt = net_total - cumulative_threshold
|
||||
|
||||
if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):
|
||||
|
||||
@@ -121,46 +121,6 @@ class TestTaxWithholdingCategory(FrappeTestCase):
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_cumulative_threshold_with_party_ledger_amount_on_net_total(self):
|
||||
invoices = []
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier3", "tax_withholding_category", "Advance TDS Category"
|
||||
)
|
||||
|
||||
# Invoice with tax and without exceeding single and cumulative thresholds
|
||||
for _ in range(2):
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=1000, do_not_save=True)
|
||||
pi.apply_tds = 1
|
||||
pi.append(
|
||||
"taxes",
|
||||
{
|
||||
"category": "Total",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"tax_amount": 500,
|
||||
"description": "Test",
|
||||
"add_deduct_tax": "Add",
|
||||
},
|
||||
)
|
||||
pi.save()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
# Third Invoice exceeds single threshold and not exceeding cumulative threshold
|
||||
pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=6000)
|
||||
pi1.apply_tds = 1
|
||||
pi1.save()
|
||||
pi1.submit()
|
||||
invoices.append(pi1)
|
||||
|
||||
# Cumulative threshold is 10,000
|
||||
# Threshold calculation should be only on the third invoice
|
||||
self.assertEqual(pi1.taxes[0].tax_amount, 800)
|
||||
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_cumulative_threshold_tcs(self):
|
||||
frappe.db.set_value(
|
||||
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
|
||||
|
||||
@@ -526,8 +526,7 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, inclu
|
||||
tax_details = frappe.db.sql(
|
||||
"""select parent, account_head,
|
||||
sum(base_tax_amount_after_discount_amount) as tax_amount
|
||||
from `tabSales Taxes and Charges` where parent in (%s) and parenttype = 'Sales Invoice'
|
||||
group by parent, account_head"""
|
||||
from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head"""
|
||||
% ", ".join(["%s"] * len(invoice_list)),
|
||||
tuple(inv.name for inv in invoice_list),
|
||||
as_dict=1,
|
||||
|
||||
@@ -1969,7 +1969,6 @@ class QueryPaymentLedger:
|
||||
ple.cost_center.as_("cost_center"),
|
||||
Sum(ple.amount).as_("amount"),
|
||||
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
|
||||
ple.remarks,
|
||||
)
|
||||
.where(ple.delinked == 0)
|
||||
.where(Criterion.all(filter_on_voucher_no))
|
||||
@@ -2032,7 +2031,6 @@ class QueryPaymentLedger:
|
||||
Table("vouchers").due_date,
|
||||
Table("vouchers").currency,
|
||||
Table("vouchers").cost_center.as_("cost_center"),
|
||||
Table("vouchers").remarks,
|
||||
)
|
||||
.where(Criterion.all(filter_on_outstanding_amount))
|
||||
)
|
||||
|
||||
@@ -790,19 +790,14 @@ class Asset(AccountsController):
|
||||
args.get("value_after_depreciation")
|
||||
)
|
||||
else:
|
||||
value = flt(args.get("expected_value_after_useful_life")) / (
|
||||
flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
|
||||
)
|
||||
value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount)
|
||||
|
||||
depreciation_rate = math.pow(
|
||||
value,
|
||||
1.0
|
||||
/ (
|
||||
(
|
||||
(
|
||||
flt(args.get("total_number_of_depreciations"), 2)
|
||||
- flt(self.opening_number_of_booked_depreciations)
|
||||
)
|
||||
flt(args.get("total_number_of_depreciations"), 2)
|
||||
* flt(args.get("frequency_of_depreciation"))
|
||||
)
|
||||
/ 12
|
||||
|
||||
@@ -79,9 +79,6 @@ def validate_returned_items(doc):
|
||||
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
|
||||
select_fields += ",rejected_qty, received_qty"
|
||||
|
||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
select_fields += ",name"
|
||||
|
||||
for d in frappe.db.sql(
|
||||
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
|
||||
doc.return_against,
|
||||
@@ -107,24 +104,15 @@ def validate_returned_items(doc):
|
||||
|
||||
items_returned = False
|
||||
for d in doc.get("items"):
|
||||
key = d.item_code
|
||||
raise_exception = False
|
||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
field = frappe.scrub(doc.doctype) + "_item"
|
||||
if d.get(field):
|
||||
key = (d.item_code, d.get(field))
|
||||
raise_exception = True
|
||||
|
||||
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
|
||||
if key not in valid_items:
|
||||
frappe.msgprint(
|
||||
if d.item_code not in valid_items:
|
||||
frappe.throw(
|
||||
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
|
||||
d.idx, d.item_code, doc.doctype, doc.return_against
|
||||
),
|
||||
raise_exception=raise_exception,
|
||||
)
|
||||
)
|
||||
else:
|
||||
ref = valid_items.get(key, frappe._dict())
|
||||
ref = valid_items.get(d.item_code, frappe._dict())
|
||||
validate_quantity(doc, d, ref, valid_items, already_returned_items)
|
||||
|
||||
if (
|
||||
@@ -205,12 +193,8 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
||||
def get_ref_item_dict(valid_items, ref_item_row):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
key = ref_item_row.item_code
|
||||
if ref_item_row.get("name"):
|
||||
key = (ref_item_row.item_code, ref_item_row.name)
|
||||
|
||||
valid_items.setdefault(
|
||||
key,
|
||||
ref_item_row.item_code,
|
||||
frappe._dict(
|
||||
{
|
||||
"qty": 0,
|
||||
@@ -224,7 +208,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
|
||||
}
|
||||
),
|
||||
)
|
||||
item_dict = valid_items[key]
|
||||
item_dict = valid_items[ref_item_row.item_code]
|
||||
item_dict["qty"] += ref_item_row.qty
|
||||
item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0)
|
||||
if ref_item_row.get("rate", 0) > item_dict["rate"]:
|
||||
|
||||
@@ -561,11 +561,11 @@ class SubcontractingController(StockController):
|
||||
use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
|
||||
|
||||
if self.doctype == self.subcontract_data.order_doctype:
|
||||
rm_obj.required_qty = flt(qty, rm_obj.precision("required_qty"))
|
||||
rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount"))
|
||||
rm_obj.required_qty = qty
|
||||
rm_obj.amount = rm_obj.required_qty * rm_obj.rate
|
||||
else:
|
||||
rm_obj.consumed_qty = flt(qty, rm_obj.precision("consumed_qty"))
|
||||
rm_obj.required_qty = flt(bom_item.required_qty or qty, rm_obj.precision("required_qty"))
|
||||
rm_obj.consumed_qty = qty
|
||||
rm_obj.required_qty = bom_item.required_qty or qty
|
||||
rm_obj.serial_and_batch_bundle = None
|
||||
setattr(
|
||||
rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field)
|
||||
@@ -664,8 +664,8 @@ class SubcontractingController(StockController):
|
||||
self.__set_serial_nos(item_row, rm_obj)
|
||||
|
||||
def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
|
||||
rm_obj.required_qty = flt(required_qty, rm_obj.precision("required_qty"))
|
||||
rm_obj.consumed_qty = flt(consumed_qty, rm_obj.precision("consumed_qty"))
|
||||
rm_obj.required_qty = required_qty
|
||||
rm_obj.consumed_qty = consumed_qty
|
||||
|
||||
def __set_serial_nos(self, item_row, rm_obj):
|
||||
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
|
||||
|
||||
@@ -1234,7 +1234,6 @@ def make_subcontracted_items():
|
||||
"Subcontracted Item SA6": {},
|
||||
"Subcontracted Item SA7": {},
|
||||
"Subcontracted Item SA8": {},
|
||||
"Subcontracted Item SA9": {"stock_uom": "Litre"},
|
||||
}
|
||||
|
||||
for item, properties in sub_contracted_items.items():
|
||||
@@ -1255,7 +1254,6 @@ def make_raw_materials():
|
||||
"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
|
||||
"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"},
|
||||
"Subcontracted SRM Item 8": {},
|
||||
"Subcontracted SRM Item 9": {"stock_uom": "Litre"},
|
||||
}
|
||||
|
||||
for item, properties in raw_materials.items():
|
||||
@@ -1282,7 +1280,6 @@ def make_service_items():
|
||||
"Subcontracted Service Item 6": {},
|
||||
"Subcontracted Service Item 7": {},
|
||||
"Subcontracted Service Item 8": {},
|
||||
"Subcontracted Service Item 9": {},
|
||||
}
|
||||
|
||||
for item, properties in service_items.items():
|
||||
|
||||
@@ -127,8 +127,8 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
|
||||
fieldname: "prospect_name",
|
||||
fieldtype: "Data",
|
||||
default: frm.doc.company_name,
|
||||
reqd: 1,
|
||||
depends_on: "create_prospect",
|
||||
mandatory_depends_on: "create_prospect",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -921,24 +921,6 @@ class JobCard(Document):
|
||||
if doc.transfer_material_against == "Work Order" or doc.skip_transfer:
|
||||
return
|
||||
|
||||
if self.items:
|
||||
# sum of 'For Quantity' of Stock Entries against JC
|
||||
self.transferred_qty = (
|
||||
frappe.db.get_value(
|
||||
"Stock Entry",
|
||||
{
|
||||
"job_card": self.name,
|
||||
"work_order": self.work_order,
|
||||
"docstatus": 1,
|
||||
"purpose": "Material Transfer for Manufacture",
|
||||
},
|
||||
"sum(fg_completed_qty)",
|
||||
)
|
||||
or 0
|
||||
)
|
||||
|
||||
self.db_set("transferred_qty", self.transferred_qty)
|
||||
|
||||
qty = 0
|
||||
if self.work_order:
|
||||
if doc.transfer_material_against == "Job Card" and not doc.skip_transfer:
|
||||
|
||||
@@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
|
||||
erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
|
||||
erpnext.patches.v14_0.set_period_start_end_date_in_pcv
|
||||
erpnext.patches.v14_0.update_closing_balances #29-10-2024
|
||||
erpnext.patches.v14_0.update_closing_balances #14-07-2023
|
||||
execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
|
||||
erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts
|
||||
erpnext.patches.v14_0.update_subscription_details
|
||||
|
||||
@@ -24,11 +24,10 @@ def execute():
|
||||
for pcv in get_period_closing_vouchers(company):
|
||||
company_wise_order.setdefault(pcv.company, [])
|
||||
if pcv.period_end_date not in company_wise_order[pcv.company]:
|
||||
pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name)
|
||||
pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields)
|
||||
pcv_doc.closing_account_gle = get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields)
|
||||
closing_entries = pcv_doc.get_account_closing_balances()
|
||||
make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date)
|
||||
pcv.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields)
|
||||
closing_entries = pcv.get_account_closing_balances()
|
||||
if closing_entries:
|
||||
make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date)
|
||||
|
||||
company_wise_order[pcv.company].append(pcv.period_end_date)
|
||||
i += 1
|
||||
@@ -39,7 +38,6 @@ def get_gle_fields():
|
||||
accounting_dimension_fields = get_accounting_dimensions()
|
||||
gle_fields = [
|
||||
"name",
|
||||
"company",
|
||||
"posting_date",
|
||||
"account",
|
||||
"account_currency",
|
||||
@@ -63,27 +61,14 @@ def get_period_closing_vouchers(company):
|
||||
)
|
||||
|
||||
|
||||
def get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields):
|
||||
return get_gl_entries(pcv, gle_fields, {"account": ["!=", pcv.closing_account_head]})
|
||||
|
||||
|
||||
def get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields):
|
||||
return get_gl_entries(pcv, gle_fields, {"account": pcv.closing_account_head})
|
||||
|
||||
|
||||
def get_gl_entries(pcv, gle_fields, accounts_filter=None):
|
||||
filters = {"voucher_no": pcv.name, "is_cancelled": 0}
|
||||
if accounts_filter:
|
||||
filters.update(accounts_filter)
|
||||
|
||||
def get_pcv_gl_entries(pcv, gle_fields):
|
||||
gl_entries = frappe.db.get_all(
|
||||
"GL Entry",
|
||||
filters=filters,
|
||||
filters={"voucher_no": pcv.name, "account": ["!=", pcv.closing_account_head], "is_cancelled": 0},
|
||||
fields=gle_fields,
|
||||
)
|
||||
for entry in gl_entries:
|
||||
entry["is_period_closing_voucher_entry"] = 1
|
||||
entry["closing_date"] = pcv.period_end_date
|
||||
entry["period_closing_voucher"] = pcv.name
|
||||
|
||||
return gl_entries
|
||||
|
||||
@@ -93,14 +93,14 @@ class Project(Document):
|
||||
|
||||
def validate(self):
|
||||
if not self.is_new():
|
||||
self.copy_from_template() # nosemgrep
|
||||
self.copy_from_template()
|
||||
self.send_welcome_email()
|
||||
self.update_costing()
|
||||
self.update_percent_complete()
|
||||
self.validate_from_to_dates("expected_start_date", "expected_end_date")
|
||||
self.validate_from_to_dates("actual_start_date", "actual_end_date")
|
||||
|
||||
def copy_from_template(self): # nosemgrep
|
||||
def copy_from_template(self):
|
||||
"""
|
||||
Copy tasks from template
|
||||
"""
|
||||
@@ -205,7 +205,7 @@ class Project(Document):
|
||||
self.db_update()
|
||||
|
||||
def after_insert(self):
|
||||
self.copy_from_template() # nosemgrep
|
||||
self.copy_from_template()
|
||||
if self.sales_order:
|
||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||
|
||||
@@ -324,13 +324,9 @@ class Project(Document):
|
||||
self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
|
||||
|
||||
def update_billed_amount(self):
|
||||
# nosemgrep
|
||||
total_billed_amount = frappe.db.sql(
|
||||
"""select sum(base_net_amount)
|
||||
from `tabSales Invoice Item` si_item, `tabSales Invoice` si
|
||||
where si_item.parent = si.name
|
||||
and if(si_item.project, si_item.project, si.project) = %s
|
||||
and si.docstatus=1""",
|
||||
"""select sum(base_net_total)
|
||||
from `tabSales Invoice` where project = %s and docstatus=1""",
|
||||
self.name,
|
||||
)
|
||||
|
||||
@@ -680,8 +676,31 @@ def update_project_sales_billing():
|
||||
return
|
||||
|
||||
# Else simply fallback to Daily
|
||||
for project in frappe.get_all("Project", filters={"status": ["!=", "Cancelled"]}):
|
||||
frappe.get_doc("Project", project.name).save()
|
||||
exists_query = "(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)"
|
||||
project_map = {}
|
||||
for project_details in frappe.db.sql(
|
||||
"""
|
||||
SELECT name, 1 as order_exists, null as invoice_exists from `tabProject` where
|
||||
exists {order_exists}
|
||||
union
|
||||
SELECT name, null as order_exists, 1 as invoice_exists from `tabProject` where
|
||||
exists {invoice_exists}
|
||||
""".format(
|
||||
order_exists=exists_query.format(doctype="Sales Order"),
|
||||
invoice_exists=exists_query.format(doctype="Sales Invoice"),
|
||||
),
|
||||
as_dict=True,
|
||||
):
|
||||
project = project_map.setdefault(
|
||||
project_details.name, frappe.get_doc("Project", project_details.name)
|
||||
)
|
||||
if project_details.order_exists:
|
||||
project.update_sales_amount()
|
||||
if project_details.invoice_exists:
|
||||
project.update_billed_amount()
|
||||
|
||||
for project in project_map.values():
|
||||
project.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -732,6 +751,7 @@ def get_users_email(doc):
|
||||
def calculate_total_purchase_cost(project: str | None = None):
|
||||
if project:
|
||||
pitem = qb.DocType("Purchase Invoice Item")
|
||||
frappe.qb.DocType("Purchase Invoice Item")
|
||||
total_purchase_cost = (
|
||||
qb.from_(pitem)
|
||||
.select(Sum(pitem.base_net_amount))
|
||||
|
||||
@@ -22,46 +22,6 @@ class OverWorkLoggedError(frappe.ValidationError):
|
||||
|
||||
|
||||
class Timesheet(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
from erpnext.projects.doctype.timesheet_detail.timesheet_detail import TimesheetDetail
|
||||
|
||||
amended_from: DF.Link | None
|
||||
base_total_billable_amount: DF.Currency
|
||||
base_total_billed_amount: DF.Currency
|
||||
base_total_costing_amount: DF.Currency
|
||||
company: DF.Link | None
|
||||
currency: DF.Link | None
|
||||
customer: DF.Link | None
|
||||
department: DF.Link | None
|
||||
employee: DF.Link | None
|
||||
employee_name: DF.Data | None
|
||||
end_date: DF.Date | None
|
||||
exchange_rate: DF.Float
|
||||
naming_series: DF.Literal["TS-.YYYY.-"]
|
||||
note: DF.TextEditor | None
|
||||
parent_project: DF.Link | None
|
||||
per_billed: DF.Percent
|
||||
sales_invoice: DF.Link | None
|
||||
start_date: DF.Date | None
|
||||
status: DF.Literal["Draft", "Submitted", "Billed", "Payslip", "Completed", "Cancelled"]
|
||||
time_logs: DF.Table[TimesheetDetail]
|
||||
title: DF.Data | None
|
||||
total_billable_amount: DF.Currency
|
||||
total_billable_hours: DF.Float
|
||||
total_billed_amount: DF.Currency
|
||||
total_billed_hours: DF.Float
|
||||
total_costing_amount: DF.Currency
|
||||
total_hours: DF.Float
|
||||
user: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
self.set_status()
|
||||
self.validate_dates()
|
||||
|
||||
@@ -15,7 +15,6 @@ def execute(filters=None):
|
||||
filters=filters,
|
||||
fields=[
|
||||
"name",
|
||||
"project_name",
|
||||
"status",
|
||||
"percent_complete",
|
||||
"expected_start_date",
|
||||
@@ -49,11 +48,6 @@ def get_columns():
|
||||
"options": "Project",
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"fieldname": "project_name",
|
||||
"label": _("Project Name"),
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"fieldname": "project_type",
|
||||
"label": _("Type"),
|
||||
@@ -88,7 +82,7 @@ def get_chart_data(data):
|
||||
overdue = []
|
||||
|
||||
for project in data:
|
||||
labels.append(project.project_name)
|
||||
labels.append(project.name)
|
||||
total.append(project.total_tasks)
|
||||
completed.append(project.completed_tasks)
|
||||
overdue.append(project.overdue_tasks)
|
||||
|
||||
@@ -375,8 +375,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
recipient_id: me.frm.doc.contact_email,
|
||||
payment_request_type: payment_request_type,
|
||||
party_type: payment_request_type == 'Outward' ? "Supplier" : "Customer",
|
||||
party: payment_request_type == 'Outward' ? me.frm.doc.supplier : me.frm.doc.customer,
|
||||
party_name:payment_request_type == 'Outward' ? me.frm.doc.supplier_name : me.frm.doc.customer_name
|
||||
party: payment_request_type == 'Outward' ? me.frm.doc.supplier : me.frm.doc.customer
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc){
|
||||
|
||||
@@ -547,8 +547,6 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
async on_cart_update(args) {
|
||||
frappe.dom.freeze();
|
||||
if (this.frm.doc.set_warehouse != this.settings.warehouse)
|
||||
this.frm.doc.set_warehouse = this.settings.warehouse;
|
||||
let item_row = undefined;
|
||||
try {
|
||||
let { field, value, item } = args;
|
||||
|
||||
@@ -350,11 +350,6 @@ erpnext.PointOfSale.Payment = class {
|
||||
}
|
||||
|
||||
checkout() {
|
||||
const frm = this.events.get_frm();
|
||||
frm.cscript.calculate_outstanding_amount();
|
||||
frm.refresh_field("outstanding_amount");
|
||||
frm.refresh_field("paid_amount");
|
||||
frm.refresh_field("base_paid_amount");
|
||||
this.events.toggle_other_sections(true);
|
||||
this.toggle_component(true);
|
||||
|
||||
|
||||
@@ -1020,6 +1020,7 @@ def make_sales_invoice(source_name, target_doc=None, args=None):
|
||||
"parent": "delivery_note",
|
||||
"so_detail": "so_detail",
|
||||
"against_sales_order": "sales_order",
|
||||
"serial_no": "serial_no",
|
||||
"cost_center": "cost_center",
|
||||
},
|
||||
"postprocess": update_item,
|
||||
|
||||
@@ -25,7 +25,6 @@ from erpnext.controllers.tests.test_subcontracting_controller import (
|
||||
make_subcontracted_items,
|
||||
set_backflush_based_on,
|
||||
)
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
|
||||
@@ -684,28 +683,6 @@ class TestSubcontractingOrder(FrappeTestCase):
|
||||
|
||||
self.assertEqual(requested_qty, new_requested_qty)
|
||||
|
||||
def test_subcontracting_order_rm_required_items_for_precision(self):
|
||||
item_code = "Subcontracted Item SA9"
|
||||
raw_materials = ["Subcontracted SRM Item 9"]
|
||||
if not frappe.db.exists("BOM", {"item": item_code}):
|
||||
make_bom(item=item_code, raw_materials=raw_materials, rate=100, rm_qty=1.04)
|
||||
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Subcontracted Service Item 9",
|
||||
"qty": 1, # 202.0656,
|
||||
"rate": 100,
|
||||
"fg_item": "Subcontracted Item SA9",
|
||||
"fg_item_qty": 202.0656,
|
||||
},
|
||||
]
|
||||
|
||||
sco = get_subcontracting_order(service_items=service_items)
|
||||
sco.reload()
|
||||
|
||||
self.assertEqual(sco.supplied_items[0].required_qty, 210.149)
|
||||
|
||||
|
||||
def create_subcontracting_order(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -331,12 +331,9 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
supplied_items_details[item.name] = {}
|
||||
|
||||
for supplied_item in supplied_items:
|
||||
if supplied_item.rm_item_code not in supplied_items_details[item.name]:
|
||||
supplied_items_details[item.name][supplied_item.rm_item_code] = 0.0
|
||||
|
||||
supplied_items_details[item.name][
|
||||
supplied_item.rm_item_code
|
||||
] += supplied_item.available_qty
|
||||
] = supplied_item.available_qty
|
||||
else:
|
||||
for item in self.get("supplied_items"):
|
||||
item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get(
|
||||
|
||||
@@ -173,11 +173,11 @@
|
||||
<div class="col-xs-8 col-sm-10">
|
||||
{{ d.item_code }}
|
||||
<div class="text-muted small item-description">
|
||||
{{ html2text(d.description or "") | truncate(140) }}
|
||||
{{ html2text(d.description) | truncate(140) }}
|
||||
</div>
|
||||
<span class="text-muted mt-2 d-l-n order-qty">
|
||||
{{ _("Qty ") }}({{ d.get_formatted("qty") }})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
Reference in New Issue
Block a user