Merge pull request #43891 from frappe/version-15-hotfix

chore: release v15
This commit is contained in:
ruthra kumar
2024-10-30 15:23:57 +05:30
committed by GitHub
47 changed files with 8620 additions and 122 deletions

View File

@@ -58,7 +58,7 @@ frappe.ui.form.on("Accounting Dimension", {
}, },
label: function (frm) { label: function (frm) {
frm.set_value("fieldname", frappe.model.scrub(frm.doc.label)); frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase());
}, },
document_type: function (frm) { document_type: function (frm) {

View File

@@ -7,6 +7,7 @@ import json
import frappe import frappe
from frappe import _, scrub from frappe import _, scrub
from frappe.custom.doctype.custom_field.custom_field import create_custom_field 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 import core_doctypes_list
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cstr from frappe.utils import cstr
@@ -60,6 +61,7 @@ class AccountingDimension(Document):
if not self.is_new(): if not self.is_new():
self.validate_document_type_change() self.validate_document_type_change()
validate_column_name(self.fieldname)
self.validate_dimension_defaults() self.validate_dimension_defaults()
def validate_document_type_change(self): def validate_document_type_change(self):

View File

@@ -323,6 +323,7 @@ class PaymentReconciliation(Document):
"posting_date": inv.posting_date, "posting_date": inv.posting_date,
"currency": inv.currency, "currency": inv.currency,
"cost_center": inv.cost_center, "cost_center": inv.cost_center,
"remarks": inv.remarks,
} }
) )
) )

View File

@@ -14,7 +14,7 @@
"amount", "amount",
"difference_amount", "difference_amount",
"sec_break1", "sec_break1",
"remark", "remarks",
"currency", "currency",
"exchange_rate", "exchange_rate",
"cost_center" "cost_center"
@@ -74,12 +74,6 @@
"fieldname": "sec_break1", "fieldname": "sec_break1",
"fieldtype": "Section Break" "fieldtype": "Section Break"
}, },
{
"fieldname": "remark",
"fieldtype": "Small Text",
"label": "Remark",
"read_only": 1
},
{ {
"fieldname": "currency", "fieldname": "currency",
"fieldtype": "Link", "fieldtype": "Link",
@@ -105,12 +99,18 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Cost Center", "label": "Cost Center",
"options": "Cost Center" "options": "Cost Center"
},
{
"fieldname": "remarks",
"fieldtype": "Small Text",
"label": "Remarks",
"read_only": 1
} }
], ],
"is_virtual": 1, "is_virtual": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2023-11-17 17:33:34.818530", "modified": "2024-10-29 16:24:43.021230",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Reconciliation Payment", "name": "Payment Reconciliation Payment",

View File

@@ -27,7 +27,7 @@ class PaymentReconciliationPayment(Document):
reference_name: DF.DynamicLink | None reference_name: DF.DynamicLink | None
reference_row: DF.Data | None reference_row: DF.Data | None
reference_type: DF.Link | None reference_type: DF.Link | None
remark: DF.SmallText | None remarks: DF.SmallText | None
# end: auto-generated types # end: auto-generated types
@staticmethod @staticmethod

View File

@@ -14,6 +14,7 @@
"party_details", "party_details",
"party_type", "party_type",
"party", "party",
"party_name",
"column_break_4", "column_break_4",
"reference_doctype", "reference_doctype",
"reference_name", "reference_name",
@@ -422,13 +423,19 @@
"label": "Party Account Currency", "label": "Party Account Currency",
"options": "Currency", "options": "Currency",
"read_only": 1 "read_only": 1
},
{
"fieldname": "party_name",
"fieldtype": "Data",
"label": "Party Name",
"read_only": 1
} }
], ],
"in_create": 1, "in_create": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-09-16 17:50:54.440090", "modified": "2024-10-23 12:23:40.117336",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Request", "name": "Payment Request",

View File

@@ -71,6 +71,7 @@ class PaymentRequest(Document):
outstanding_amount: DF.Currency outstanding_amount: DF.Currency
party: DF.DynamicLink | None party: DF.DynamicLink | None
party_account_currency: DF.Link | None party_account_currency: DF.Link | None
party_name: DF.Data | None
party_type: DF.Link | None party_type: DF.Link | None
payment_account: DF.ReadOnly | None payment_account: DF.ReadOnly | None
payment_channel: DF.Literal["", "Email", "Phone", "Other"] payment_channel: DF.Literal["", "Email", "Phone", "Other"]
@@ -303,12 +304,12 @@ class PaymentRequest(Document):
return controller.get_payment_url( return controller.get_payment_url(
**{ **{
"amount": flt(self.grand_total, self.precision("grand_total")), "amount": flt(self.grand_total, self.precision("grand_total")),
"title": data.company.encode("utf-8"), "title": data.company,
"description": self.subject.encode("utf-8"), "description": self.subject,
"reference_doctype": "Payment Request", "reference_doctype": "Payment Request",
"reference_docname": self.name, "reference_docname": self.name,
"payer_email": self.email_to or frappe.session.user, "payer_email": self.email_to or frappe.session.user,
"payer_name": frappe.safe_encode(data.customer_name), "payer_name": data.customer_name,
"order_id": self.name, "order_id": self.name,
"currency": self.currency, "currency": self.currency,
} }
@@ -609,6 +610,7 @@ def make_payment_request(**args):
"party_type": party_type, "party_type": party_type,
"party": args.get("party") or ref_doc.get("customer"), "party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account, "bank_account": bank_account,
"party_name": args.get("party_name") or ref_doc.get("customer_name"),
} }
) )

View File

@@ -413,11 +413,16 @@ class PeriodClosingVoucher(AccountsController):
return closing_entries return closing_entries
def is_first_period_closing_voucher(self): def is_first_period_closing_voucher(self):
return not frappe.db.exists( first_pcv = frappe.db.get_value(
"Period Closing Voucher", "Period Closing Voucher",
{"company": self.company, "docstatus": 1, "name": ("!=", self.name)}, {"company": self.company, "docstatus": 1},
"name",
order_by="period_end_date",
) )
if not first_pcv or first_pcv == self.name:
return True
def cancel_gl_entries(self): def cancel_gl_entries(self):
if self.get_gle_count_against_current_pcv() > 5000: if self.get_gle_count_against_current_pcv() > 5000:
frappe.enqueue( frappe.enqueue(

View File

@@ -31,6 +31,13 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}, },
}; };
}); });
this.frm.set_query("expense_account", "items", function () {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: doc.company },
};
});
} }
onload() { onload() {
@@ -335,7 +342,9 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
party_type: "Supplier", party_type: "Supplier",
account: this.frm.doc.credit_to, account: this.frm.doc.credit_to,
price_list: this.frm.doc.buying_price_list, price_list: this.frm.doc.buying_price_list,
fetch_payment_terms_template: cint(!this.frm.doc.ignore_default_payment_terms_template), fetch_payment_terms_template: cint(
(this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template
),
}, },
function () { function () {
me.apply_pricing_rule(); me.apply_pricing_rule();
@@ -506,13 +515,6 @@ cur_frm.fields_dict["select_print_heading"].get_query = function (doc, cdt, cdn)
}; };
}; };
cur_frm.set_query("expense_account", "items", function (doc) {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: doc.company },
};
});
cur_frm.set_query("wip_composite_asset", "items", function () { cur_frm.set_query("wip_composite_asset", "items", function () {
return { return {
filters: { is_composite_asset: 1, docstatus: 0 }, filters: { is_composite_asset: 1, docstatus: 0 },

View File

@@ -1134,12 +1134,14 @@
"label": "Payment Terms" "label": "Payment Terms"
}, },
{ {
"depends_on": "eval:(!doc.is_paid && !doc.is_return)",
"fieldname": "payment_terms_template", "fieldname": "payment_terms_template",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Payment Terms Template", "label": "Payment Terms Template",
"options": "Payment Terms Template" "options": "Payment Terms Template"
}, },
{ {
"depends_on": "eval:(!doc.is_paid && !doc.is_return)",
"fieldname": "payment_schedule", "fieldname": "payment_schedule",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Payment Schedule", "label": "Payment Schedule",
@@ -1631,7 +1633,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-09-11 12:59:19.130593", "modified": "2024-10-25 18:13:01.944477",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@@ -1593,7 +1593,11 @@ class PurchaseInvoice(BuyingController):
for proj, value in projects.items(): for proj, value in projects.items():
res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run() 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 current_purchase_cost = res and res[0][0] or 0
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value) # 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()
def validate_supplier_invoice(self): def validate_supplier_invoice(self):
if self.bill_date: if self.bill_date:

View File

@@ -505,7 +505,8 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
"options": "Project", "options": "Project",
"print_hide": 1 "print_hide": 1,
"search_index": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@@ -974,7 +975,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-07-19 12:12:42.449298", "modified": "2024-10-28 15:06:19.246141",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@@ -339,6 +339,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
account: this.frm.doc.debit_to, account: this.frm.doc.debit_to,
price_list: this.frm.doc.selling_price_list, price_list: this.frm.doc.selling_price_list,
pos_profile: pos_profile, pos_profile: pos_profile,
fetch_payment_terms_template: cint(
(this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template
),
}, },
function () { function () {
me.apply_pricing_rule(); me.apply_pricing_rule();

View File

@@ -1734,9 +1734,11 @@ class SalesInvoice(SellingController):
) )
def update_project(self): def update_project(self):
if self.project: unique_projects = list(set([d.project for d in self.get("items") if d.project]))
project = frappe.get_doc("Project", self.project) for p in unique_projects:
project = frappe.get_doc("Project", p)
project.update_billed_amount() project.update_billed_amount()
project.calculate_gross_margin()
project.db_update() project.db_update()
def verify_payment_amount_is_positive(self): def verify_payment_amount_is_positive(self):

View File

@@ -812,7 +812,8 @@
"fieldname": "project", "fieldname": "project",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
"options": "Project" "options": "Project",
"search_index": 1
}, },
{ {
"depends_on": "eval:parent.update_stock == 1", "depends_on": "eval:parent.update_stock == 1",
@@ -927,7 +928,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-05-23 16:36:18.970862", "modified": "2024-10-28 15:06:40.980995",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@@ -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("apply_tax_withholding_amount", None)
payment_entry_filters.pop("tax_withholding_category", None) payment_entry_filters.pop("tax_withholding_category", None)
supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0 supp_inv_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
supp_jv_credit_amt = ( supp_jv_credit_amt = (
frappe.db.get_value( frappe.db.get_value(
@@ -538,7 +538,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
group_by="payment_type", 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 supp_credit_amt += inv.tax_withholding_net_total
for type in payment_entry_amounts: 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 tax_withholding_net_total = inv.tax_withholding_net_total
if (threshold and tax_withholding_net_total >= threshold) or ( if (threshold and tax_withholding_net_total >= threshold) or (
cumulative_threshold and supp_credit_amt >= cumulative_threshold cumulative_threshold and (supp_credit_amt + supp_inv_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( if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
tax_details.tax_on_excess_amount 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 supp_credit_amt = net_total - cumulative_threshold
if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0): if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):

View File

@@ -121,6 +121,46 @@ class TestTaxWithholdingCategory(FrappeTestCase):
for d in reversed(invoices): for d in reversed(invoices):
d.cancel() 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): def test_cumulative_threshold_tcs(self):
frappe.db.set_value( frappe.db.set_value(
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS" "Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"

View File

@@ -522,7 +522,8 @@ class ReceivablePayableReport:
from `tab{row.voucher_type}` si, `tabPayment Schedule` ps from `tab{row.voucher_type}` si, `tabPayment Schedule` ps
where where
si.name = ps.parent and si.name = ps.parent and
si.name = %s si.name = %s and
si.is_return = 0
order by ps.paid_amount desc, due_date order by ps.paid_amount desc, due_date
""", """,
row.voucher_no, row.voucher_no,

View File

@@ -348,10 +348,18 @@ def get_accounts_with_children(accounts):
return frappe.qb.from_(doctype).select(doctype.name).where(Criterion.any(conditions)).run(pluck=True) return frappe.qb.from_(doctype).select(doctype.name).where(Criterion.any(conditions)).run(pluck=True)
def set_bill_no(gl_entries):
inv_details = get_supplier_invoice_details()
for gl in gl_entries:
gl["bill_no"] = inv_details.get(gl.get("against_voucher"), "")
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries): def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
data = [] data = []
totals_dict = get_totals_dict() totals_dict = get_totals_dict()
set_bill_no(gl_entries)
gle_map = initialize_gle_map(gl_entries, filters, totals_dict) gle_map = initialize_gle_map(gl_entries, filters, totals_dict)
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, totals_dict) totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, totals_dict)
@@ -539,7 +547,6 @@ def get_account_type_map(company):
def get_result_as_list(data, filters): def get_result_as_list(data, filters):
balance, _balance_in_account_currency = 0, 0 balance, _balance_in_account_currency = 0, 0
inv_details = get_supplier_invoice_details()
for d in data: for d in data:
if not d.get("posting_date"): if not d.get("posting_date"):
@@ -549,7 +556,6 @@ def get_result_as_list(data, filters):
d["balance"] = balance d["balance"] = balance
d["account_currency"] = filters.account_currency d["account_currency"] = filters.account_currency
d["bill_no"] = inv_details.get(d.get("against_voucher"), "")
return data return data

View File

@@ -130,6 +130,7 @@ class PaymentLedger:
) )
def get_columns(self): def get_columns(self):
company_currency = frappe.get_cached_value("Company", self.filters.get("company"), "default_currency")
options = None options = None
self.columns.append( self.columns.append(
dict( dict(
@@ -194,7 +195,7 @@ class PaymentLedger:
label=_("Amount"), label=_("Amount"),
fieldname="amount", fieldname="amount",
fieldtype="Currency", fieldtype="Currency",
options="Company:company:default_currency", options=company_currency,
width="100", width="100",
) )
) )

View File

@@ -526,7 +526,8 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, inclu
tax_details = frappe.db.sql( tax_details = frappe.db.sql(
"""select parent, account_head, """select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount sum(base_tax_amount_after_discount_amount) as tax_amount
from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" from `tabSales Taxes and Charges` where parent in (%s) and parenttype = 'Sales Invoice'
group by parent, account_head"""
% ", ".join(["%s"] * len(invoice_list)), % ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list), tuple(inv.name for inv in invoice_list),
as_dict=1, as_dict=1,

View File

@@ -1969,6 +1969,7 @@ class QueryPaymentLedger:
ple.cost_center.as_("cost_center"), 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"),
ple.remarks,
) )
.where(ple.delinked == 0) .where(ple.delinked == 0)
.where(Criterion.all(filter_on_voucher_no)) .where(Criterion.all(filter_on_voucher_no))
@@ -2031,6 +2032,7 @@ class QueryPaymentLedger:
Table("vouchers").due_date, Table("vouchers").due_date,
Table("vouchers").currency, Table("vouchers").currency,
Table("vouchers").cost_center.as_("cost_center"), Table("vouchers").cost_center.as_("cost_center"),
Table("vouchers").remarks,
) )
.where(Criterion.all(filter_on_outstanding_amount)) .where(Criterion.all(filter_on_outstanding_amount))
) )

View File

@@ -790,14 +790,19 @@ class Asset(AccountsController):
args.get("value_after_depreciation") args.get("value_after_depreciation")
) )
else: else:
value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) value = flt(args.get("expected_value_after_useful_life")) / (
flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
)
depreciation_rate = math.pow( depreciation_rate = math.pow(
value, value,
1.0 1.0
/ ( / (
( (
flt(args.get("total_number_of_depreciations"), 2) (
flt(args.get("total_number_of_depreciations"), 2)
- flt(self.opening_number_of_booked_depreciations)
)
* flt(args.get("frequency_of_depreciation")) * flt(args.get("frequency_of_depreciation"))
) )
/ 12 / 12

View File

@@ -146,8 +146,8 @@ frappe.ui.form.on("Request for Quotation", {
return; return;
} }
}, },
"Download PDF for Supplier", __("Download PDF for Supplier"),
"Download" __("Download")
); );
}, },
__("Tools") __("Tools")
@@ -272,9 +272,10 @@ frappe.ui.form.on("Request for Quotation", {
}); });
}; };
dialog.fields_dict.note.$wrapper const msg = __(
.append(`<p class="small text-muted">This is a preview of the email to be sent. A PDF of the document will "This is a preview of the email to be sent. A PDF of the document will automatically be attached with the email."
automatically be attached with the email.</p>`); );
dialog.fields_dict.note.$wrapper.append(`<p class="small text-muted">${msg}</p>`);
dialog.show(); dialog.show();
}, },

View File

@@ -451,6 +451,11 @@ class AccountsController(TransactionBase):
) )
def validate_invoice_documents_schedule(self): def validate_invoice_documents_schedule(self):
if self.is_return:
self.payment_terms_template = ""
self.payment_schedule = []
return
self.validate_payment_schedule_dates() self.validate_payment_schedule_dates()
self.set_due_date() self.set_due_date()
self.set_payment_schedule() self.set_payment_schedule()
@@ -465,7 +470,7 @@ class AccountsController(TransactionBase):
self.validate_payment_schedule_amount() self.validate_payment_schedule_amount()
def validate_all_documents_schedule(self): def validate_all_documents_schedule(self):
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return: if self.doctype in ("Sales Invoice", "Purchase Invoice"):
self.validate_invoice_documents_schedule() self.validate_invoice_documents_schedule()
elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"): elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
self.validate_non_invoice_documents_schedule() self.validate_non_invoice_documents_schedule()

View File

@@ -79,6 +79,9 @@ def validate_returned_items(doc):
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
select_fields += ",rejected_qty, received_qty" select_fields += ",rejected_qty, received_qty"
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
select_fields += ",name"
for d in frappe.db.sql( for d in frappe.db.sql(
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""", f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
doc.return_against, doc.return_against,
@@ -104,15 +107,24 @@ def validate_returned_items(doc):
items_returned = False items_returned = False
for d in doc.get("items"): 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 d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
if d.item_code not in valid_items: if key not in valid_items:
frappe.throw( frappe.msgprint(
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format( _("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
d.idx, d.item_code, doc.doctype, doc.return_against d.idx, d.item_code, doc.doctype, doc.return_against
) ),
raise_exception=raise_exception,
) )
else: else:
ref = valid_items.get(d.item_code, frappe._dict()) ref = valid_items.get(key, frappe._dict())
validate_quantity(doc, d, ref, valid_items, already_returned_items) validate_quantity(doc, d, ref, valid_items, already_returned_items)
if ( if (
@@ -193,8 +205,12 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
def get_ref_item_dict(valid_items, ref_item_row): def get_ref_item_dict(valid_items, ref_item_row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos 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( valid_items.setdefault(
ref_item_row.item_code, key,
frappe._dict( frappe._dict(
{ {
"qty": 0, "qty": 0,
@@ -208,7 +224,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
} }
), ),
) )
item_dict = valid_items[ref_item_row.item_code] item_dict = valid_items[key]
item_dict["qty"] += ref_item_row.qty item_dict["qty"] += ref_item_row.qty
item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0) item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0)
if ref_item_row.get("rate", 0) > item_dict["rate"]: if ref_item_row.get("rate", 0) > item_dict["rate"]:

View File

@@ -561,11 +561,11 @@ class SubcontractingController(StockController):
use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields") use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
if self.doctype == self.subcontract_data.order_doctype: if self.doctype == self.subcontract_data.order_doctype:
rm_obj.required_qty = qty rm_obj.required_qty = flt(qty, rm_obj.precision("required_qty"))
rm_obj.amount = rm_obj.required_qty * rm_obj.rate rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount"))
else: else:
rm_obj.consumed_qty = qty rm_obj.consumed_qty = flt(qty, rm_obj.precision("consumed_qty"))
rm_obj.required_qty = bom_item.required_qty or qty rm_obj.required_qty = flt(bom_item.required_qty or qty, rm_obj.precision("required_qty"))
rm_obj.serial_and_batch_bundle = None rm_obj.serial_and_batch_bundle = None
setattr( setattr(
rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field) 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) self.__set_serial_nos(item_row, rm_obj)
def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0): def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
rm_obj.required_qty = required_qty rm_obj.required_qty = flt(required_qty, rm_obj.precision("required_qty"))
rm_obj.consumed_qty = consumed_qty rm_obj.consumed_qty = flt(consumed_qty, rm_obj.precision("consumed_qty"))
def __set_serial_nos(self, item_row, rm_obj): 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)) key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))

View File

@@ -1234,6 +1234,7 @@ def make_subcontracted_items():
"Subcontracted Item SA6": {}, "Subcontracted Item SA6": {},
"Subcontracted Item SA7": {}, "Subcontracted Item SA7": {},
"Subcontracted Item SA8": {}, "Subcontracted Item SA8": {},
"Subcontracted Item SA9": {"stock_uom": "Litre"},
} }
for item, properties in sub_contracted_items.items(): for item, properties in sub_contracted_items.items():
@@ -1254,6 +1255,7 @@ def make_raw_materials():
"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"}, "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 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"},
"Subcontracted SRM Item 8": {}, "Subcontracted SRM Item 8": {},
"Subcontracted SRM Item 9": {"stock_uom": "Litre"},
} }
for item, properties in raw_materials.items(): for item, properties in raw_materials.items():
@@ -1280,6 +1282,7 @@ def make_service_items():
"Subcontracted Service Item 6": {}, "Subcontracted Service Item 6": {},
"Subcontracted Service Item 7": {}, "Subcontracted Service Item 7": {},
"Subcontracted Service Item 8": {}, "Subcontracted Service Item 8": {},
"Subcontracted Service Item 9": {},
} }
for item, properties in service_items.items(): for item, properties in service_items.items():

View File

@@ -127,8 +127,8 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
fieldname: "prospect_name", fieldname: "prospect_name",
fieldtype: "Data", fieldtype: "Data",
default: frm.doc.company_name, default: frm.doc.company_name,
reqd: 1,
depends_on: "create_prospect", depends_on: "create_prospect",
mandatory_depends_on: "create_prospect",
} }
); );
} }

View File

@@ -941,26 +941,19 @@ class JobCard(Document):
qty = 0 qty = 0
if self.work_order: if self.work_order:
doc = frappe.get_doc("Work Order", self.work_order)
if doc.transfer_material_against == "Job Card" and not doc.skip_transfer: if doc.transfer_material_against == "Job Card" and not doc.skip_transfer:
completed = True min_qty = []
for d in doc.operations: for d in doc.operations:
if d.status != "Completed": if d.completed_qty:
completed = False min_qty.append(d.completed_qty)
else:
min_qty = []
break break
if completed: if min_qty:
job_cards = frappe.get_all( qty = min(min_qty)
"Job Card",
filters={"work_order": self.work_order, "docstatus": ("!=", 2)},
fields="sum(transferred_qty) as qty",
group_by="operation_id",
)
if job_cards: doc.db_set("material_transferred_for_manufacturing", qty)
qty = min(d.qty for d in job_cards)
doc.db_set("material_transferred_for_manufacturing", qty)
self.set_status(update_status) self.set_status(update_status)

View File

@@ -45,6 +45,7 @@ frappe.ui.form.on("Project", {
frm.set_query("sales_order", function () { frm.set_query("sales_order", function () {
var filters = { var filters = {
project: ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]], project: ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]],
company: frm.doc.company,
}; };
if (frm.doc.customer) { if (frm.doc.customer) {

View File

@@ -93,14 +93,14 @@ class Project(Document):
def validate(self): def validate(self):
if not self.is_new(): if not self.is_new():
self.copy_from_template() self.copy_from_template() # nosemgrep
self.send_welcome_email() self.send_welcome_email()
self.update_costing() self.update_costing()
self.update_percent_complete() self.update_percent_complete()
self.validate_from_to_dates("expected_start_date", "expected_end_date") self.validate_from_to_dates("expected_start_date", "expected_end_date")
self.validate_from_to_dates("actual_start_date", "actual_end_date") self.validate_from_to_dates("actual_start_date", "actual_end_date")
def copy_from_template(self): def copy_from_template(self): # nosemgrep
""" """
Copy tasks from template Copy tasks from template
""" """
@@ -205,7 +205,7 @@ class Project(Document):
self.db_update() self.db_update()
def after_insert(self): def after_insert(self):
self.copy_from_template() self.copy_from_template() # nosemgrep
if self.sales_order: if self.sales_order:
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
@@ -324,9 +324,13 @@ class Project(Document):
self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0 self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
def update_billed_amount(self): def update_billed_amount(self):
# nosemgrep
total_billed_amount = frappe.db.sql( total_billed_amount = frappe.db.sql(
"""select sum(base_net_total) """select sum(base_net_amount)
from `tabSales Invoice` where project = %s and docstatus=1""", 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""",
self.name, self.name,
) )
@@ -676,31 +680,8 @@ def update_project_sales_billing():
return return
# Else simply fallback to Daily # Else simply fallback to Daily
exists_query = "(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)" for project in frappe.get_all("Project", filters={"status": ["!=", "Cancelled"]}):
project_map = {} frappe.get_doc("Project", project.name).save()
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() @frappe.whitelist()
@@ -751,7 +732,6 @@ def get_users_email(doc):
def calculate_total_purchase_cost(project: str | None = None): def calculate_total_purchase_cost(project: str | None = None):
if project: if project:
pitem = qb.DocType("Purchase Invoice Item") pitem = qb.DocType("Purchase Invoice Item")
frappe.qb.DocType("Purchase Invoice Item")
total_purchase_cost = ( total_purchase_cost = (
qb.from_(pitem) qb.from_(pitem)
.select(Sum(pitem.base_net_amount)) .select(Sum(pitem.base_net_amount))

View File

@@ -22,6 +22,46 @@ class OverWorkLoggedError(frappe.ValidationError):
class Timesheet(Document): 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): def validate(self):
self.set_status() self.set_status()
self.validate_dates() self.validate_dates()

View File

@@ -15,6 +15,7 @@ def execute(filters=None):
filters=filters, filters=filters,
fields=[ fields=[
"name", "name",
"project_name",
"status", "status",
"percent_complete", "percent_complete",
"expected_start_date", "expected_start_date",
@@ -48,6 +49,11 @@ def get_columns():
"options": "Project", "options": "Project",
"width": 200, "width": 200,
}, },
{
"fieldname": "project_name",
"label": _("Project Name"),
"width": 200,
},
{ {
"fieldname": "project_type", "fieldname": "project_type",
"label": _("Type"), "label": _("Type"),
@@ -82,7 +88,7 @@ def get_chart_data(data):
overdue = [] overdue = []
for project in data: for project in data:
labels.append(project.name) labels.append(project.project_name)
total.append(project.total_tasks) total.append(project.total_tasks)
completed.append(project.completed_tasks) completed.append(project.completed_tasks)
overdue.append(project.overdue_tasks) overdue.append(project.overdue_tasks)

View File

@@ -375,7 +375,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
recipient_id: me.frm.doc.contact_email, recipient_id: me.frm.doc.contact_email,
payment_request_type: payment_request_type, payment_request_type: payment_request_type,
party_type: payment_request_type == 'Outward' ? "Supplier" : "Customer", party_type: payment_request_type == 'Outward' ? "Supplier" : "Customer",
party: payment_request_type == 'Outward' ? me.frm.doc.supplier : me.frm.doc.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
}, },
callback: function(r) { callback: function(r) {
if(!r.exc){ if(!r.exc){
@@ -2438,7 +2439,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
payment_terms_template() { payment_terms_template() {
var me = this; var me = this;
const doc = this.frm.doc; const doc = this.frm.doc;
if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') { if(doc.payment_terms_template && doc.doctype !== 'Delivery Note' && doc.is_return == 0) {
var posting_date = doc.posting_date || doc.transaction_date; var posting_date = doc.posting_date || doc.transaction_date;
frappe.call({ frappe.call({
method: "erpnext.controllers.accounts_controller.get_payment_terms", method: "erpnext.controllers.accounts_controller.get_payment_terms",

View File

@@ -29,6 +29,7 @@ erpnext.sales_common = {
query: "erpnext.controllers.queries.get_project_name", query: "erpnext.controllers.queries.get_project_name",
filters: { filters: {
customer: doc.customer, customer: doc.customer,
company: doc.company,
}, },
}; };
}); });

View File

@@ -547,6 +547,8 @@ erpnext.PointOfSale.Controller = class {
async on_cart_update(args) { async on_cart_update(args) {
frappe.dom.freeze(); frappe.dom.freeze();
if (this.frm.doc.set_warehouse != this.settings.warehouse)
this.frm.doc.set_warehouse = this.settings.warehouse;
let item_row = undefined; let item_row = undefined;
try { try {
let { field, value, item } = args; let { field, value, item } = args;

View File

@@ -350,6 +350,11 @@ erpnext.PointOfSale.Payment = class {
} }
checkout() { 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.events.toggle_other_sections(true);
this.toggle_component(true); this.toggle_component(true);

View File

@@ -1020,7 +1020,6 @@ def make_sales_invoice(source_name, target_doc=None, args=None):
"parent": "delivery_note", "parent": "delivery_note",
"so_detail": "so_detail", "so_detail": "so_detail",
"against_sales_order": "sales_order", "against_sales_order": "sales_order",
"serial_no": "serial_no",
"cost_center": "cost_center", "cost_center": "cost_center",
}, },
"postprocess": update_item, "postprocess": update_item,

View File

@@ -830,6 +830,15 @@ frappe.ui.form.on("Stock Entry", {
}); });
frappe.ui.form.on("Stock Entry Detail", { frappe.ui.form.on("Stock Entry Detail", {
set_basic_rate_manually(frm, cdt, cdn) {
let row = locals[cdt][cdn];
frm.fields_dict.items.grid.update_docfield_property(
"basic_rate",
"read_only",
row?.set_basic_rate_manually ? 0 : 1
);
},
qty(frm, cdt, cdn) { qty(frm, cdt, cdn) {
frm.events.set_basic_rate(frm, cdt, cdn); frm.events.set_basic_rate(frm, cdt, cdn);
}, },

View File

@@ -25,6 +25,7 @@ from erpnext.controllers.tests.test_subcontracting_controller import (
make_subcontracted_items, make_subcontracted_items,
set_backflush_based_on, 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.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
@@ -683,6 +684,28 @@ class TestSubcontractingOrder(FrappeTestCase):
self.assertEqual(requested_qty, new_requested_qty) 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): def create_subcontracting_order(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -331,9 +331,12 @@ class SubcontractingReceipt(SubcontractingController):
supplied_items_details[item.name] = {} supplied_items_details[item.name] = {}
for supplied_item in supplied_items: 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_items_details[item.name][
supplied_item.rm_item_code supplied_item.rm_item_code
] = supplied_item.available_qty ] += supplied_item.available_qty
else: else:
for item in self.get("supplied_items"): for item in self.get("supplied_items"):
item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get( item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get(

View File

@@ -173,7 +173,7 @@
<div class="col-xs-8 col-sm-10"> <div class="col-xs-8 col-sm-10">
{{ d.item_code }} {{ d.item_code }}
<div class="text-muted small item-description"> <div class="text-muted small item-description">
{{ html2text(d.description) | truncate(140) }} {{ html2text(d.description or "") | truncate(140) }}
</div> </div>
<span class="text-muted mt-2 d-l-n order-qty"> <span class="text-muted mt-2 d-l-n order-qty">
{{ _("Qty ") }}({{ d.get_formatted("qty") }}) {{ _("Qty ") }}({{ d.get_formatted("qty") }})

View File

@@ -11929,3 +11929,185 @@ Website Theme,Webseiten-Thema,
Workflow Action,Workflow-Aktion, Workflow Action,Workflow-Aktion,
Workflow State,Workflow-Status, Workflow State,Workflow-Status,
{0} is not running. Cannot trigger events for this Document,{0} läuft nicht. Ereignisse für dieses Dokument können nicht ausgelöst werden, {0} is not running. Cannot trigger events for this Document,{0} läuft nicht. Ereignisse für dieses Dokument können nicht ausgelöst werden,
"""SN-01::10"" for ""SN-01"" to ""SN-10""","""SN-01::10"" für ""SN-01"" bis ""SN-10""",
"<span class=""h4""><b>Masters &amp; Reports</b></span>","<span class=""h4""><b>Stammdaten &amp; Berichte</b></span>",
"<span class=""h4""><b>Quick Access</b></span>","<span class=""h4""><b>Schnellzugriff</b></span>",
"<span class=""h4""><b>Reports & Masters</b></span>","<span class=""h4""><b>Berichte & Stammdaten</b></span>",
"<span class=""h4""><b>Reports &amp; Masters</b></span>","<span class=""h4""><b>Berichte &amp; Stammdaten</b></span>",
"<span class=""h4""><b>Settings</b></span>","<span class=""h4""><b>Einstellungen</b></span>",
"<span class=""h4""><b>Shortcuts</b></span>","<span class=""h4""><b>Verknüpfungen</b></span>",
"<span class=""h4""><b>Your Shortcuts
</b></span>","<span class=""h4""><b>Ihre Verknüpfungen
</b></span>",
"<span class=""h4""><b>Your Shortcuts</b></span>","<span class=""h4""><b>Ihre Verknüpfungen</b></span>",
<strong>Grand Total:</strong> {0},<strong>Gesamtsumme:</strong>{0},
<strong>Outstanding Amount:</strong> {0},<strong>Ausstehender Betrag:</strong> {0},
Against Customer Order {0},Gegen Kundenauftrag {0},
Against Supplier Invoice {0},Gegen Lieferantenrechnung {0},
Ageing Range,Alterungsbereich,
Allocate Payment Request,Zahlungsanfrage zuweisen,
Amount in party's bank account currency,Betrag in der Währung des Bankkontos des Beteiligten,
Amount in transaction currency,Betrag in Transaktionswährung,
BIN Qty,BIN Menge,
BOM and Production,Stückliste und Produktion,
Balance Stock Value,Bestandswert,
Base Cost Per Unit,Grundkosten pro Einheit,
Base Rate,Basispreis,
Base Tax Withholding Net Total,Basis-Steuereinbehalt-Nettosumme,
Base Total,Basis-Summe,
Base Total Billable Amount,Basis Gesamter abrechenbarer Betrag,
Batch Expiry Date,Ablaufdatum der Charge,
Bill for Rejected Quantity in Purchase Invoice,Rechnung für abgelehnte Menge in der Eingangsrechnung,
Calculate daily depreciation using total days in depreciation period,Tägliche Abschreibung anhand der Gesamttage im Abschreibungszeitraum berechnen,
Call Schedule Row {0}: To time slot should always be ahead of From time slot.,Anrufplanzeile {0}: Das Zeitfenster Bis sollte immer vor dem Zeitfenster Von liegen.,
Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings.,Es wurde kein Standardlager für den Artikel {0} gefunden. Bitte legen Sie eines im Artikelstamm oder in den Lagereinstellungen fest.,
Cannot {0} from {2} without any negative outstanding invoice,Kann nicht {0} von {2} ohne negative ausstehende Rechnung,
Cheques and Deposits Incorrectly cleared,Falsch verrechnete Schecks und Einzahlungen,
Columns are not according to template. Please compare the uploaded file with standard template,Die Spalten stimmen nicht mit der Vorlage überein. Bitte vergleichen Sie die hochgeladene Datei mit der Standardvorlage,
Company is mandatory,Unternehmen ist obligatorisch,
Completion Date can not be before Failure Date. Please adjust the dates accordingly.,Das Fertigstellungsdatum kann nicht vor dem Ausfalldatum liegen. Bitte passen Sie die Daten entsprechend an.,
Consumed Stock Items or Consumed Asset Items are mandatory for creating new composite asset,Verbrauchte Lagerartikel oder verbrauchte Vermögensgegenstand-Artikel sind für die Erstellung obligatorisch,
Convert to Item Based Reposting,Umstellung auf artikelbasiertes Umbuchen,
Create Workstation,Arbeitsplatz erstellen,
Creating Journal Entries...,Journaleinträge erstellen...,
Creating Purchase Invoices ...,Eingangsrechnungen erstellen ...,
Creating Sales Invoices ...,Ausgangsrechnungen erstellen ...,
Currency Exchange Settings Result,Währungsumtauscheinstellungen Ergebnis,
Deal Owner,Besitzer des Deals,
Decapitalized,Dekapitalisiert,
Dependant SLE Voucher Detail No,Unterhaltsberechtigter SLE Beleg Detail Nr.,
Disassemble,Demontage,
Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.,Dokumente: {0} hat vertagte Einnahmen/Ausgaben aktiviert. Kann nicht erneut posten.,
Don't Reserve Sales Order Qty on Sales Return,Menge des Auftrags nicht bei der Rücksendung reservieren,
Enter Manually,Manuell eingeben,
Failed to post depreciation entries,Abschreibungsbuchungen fehlgeschlagen,
Filters missing,Filter fehlen,
"For Return Invoices with Stock effect, '0' qty Items are not allowed. Following rows are affected: {0}",Bei Retourenrechnungen mit Lagereffekt sind Artikel mit einer Menge von '0' nicht zulässig. Folgende Zeilen sind betroffen: {0},
"For the item {0}, the quantity should be {1} according to the BOM {2}.",Für den Artikel {0} sollte die Menge gemäß Stückliste {2} {1} betragen.,
"For the {0}, no stock is available for the return in the warehouse {1}.",Für {0} ist im Lager {1} kein Bestand für die Retoure verfügbar.,
Force-Fetch Subscription Updates,Abonnement-Updates erzwingen,
From Date is mandatory,Von-Datum ist obligatorisch,
From Prospect,Von Interessenten,
Gross Purchase Amount Too Low: {0} cannot be depreciated over {1} cycles with a frequency of {2} depreciations.,Bruttokaufbetrag zu niedrig: {0} kann nicht über {1} Zyklen mit einer Häufigkeit von {2} Abschreibungen abgeschrieben werden.,
If enabled then system won't apply the pricing rule on the delivery note which will be create from the pick list,"Falls aktiviert, wird das System die Preisregel nicht auf den Lieferschein anwenden, der aus der Pickliste erstellt wird",
Impairment,Wertminderung,
Include Closed Orders,Geschlossene Aufträge/Bestellungen einbeziehen,
Invalid Allocated Amount,Ungültiger zugewiesener Betrag,
Invalid Amount,Ungültiger Betrag,
Is Standard,Ist Standard,
Item {0} does not exist.,Artikel {0} existiert nicht.,
Items {0} do not exist in the Item master.,Artikel {0} sind nicht im Artikelstamm vorhanden.,
Journal entries have been created,Journaleinträge wurden erstellt,
Net total calculation precision loss,Präzisionsverlust bei Berechnung der Nettosumme,
Only Deduct Tax On Excess Amount ,Nur den überschüssigen Betrag versteuern ,
Payment Ledger Entry,Zahlungsbucheintrag,
Payment Requests cannot be created against: {0},Zahlungsanforderungen können nicht erstellt werden für: {0},
Period Closing Entry For Current Period,Periodenabschlussbuchung für aktuelle Periode,
Provisional Account,Vorläufiges Konto,
Rate of Stock UOM,Einzelpreis der Lager-ME,
Raw Materials Consumption ,Rohstoffverbrauch ,
Recalculating Purchase Cost against this Project...,Neuberechnung der Anschaffungskosten für dieses Projekt...,
Reference DocType,Referenz DocType,
Round Tax Amount Row-wise,Steuerbetrag zeilenweise runden,
Rounding gain/loss Entry for Stock Transfer,Rundungsgewinn/-verlustbuchung für Umlagerung,
Row #{0}: Only {1} available to reserve for the Item {2},Zeile #{0}: Nur {1} zur Reservierung für den Artikel {2} verfügbar,
Row #{}: The original Invoice {} of return invoice {} is not consolidated.,Zeile #{}: Die ursprüngliche Rechnung {} der Rechnungskorrektur {} ist nicht konsolidiert.,
Row {0}: Item {1} must be a subcontracted item.,Zeile {0}: Artikel {1} muss ein an Dritte vergebener Artikel sein.,
Row {0}: Please provide a valid Delivery Note Item or Packed Item reference.,Zeile {0}: Bitte geben Sie einen gültigen Lieferschein Artikel oder verpackten Artikel an.,
Row {0}: Target Warehouse is mandatory for internal transfers,Zeile {0}: Ziellager ist für interne Transfers obligatorisch,
Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2},Zeile({0}): Ausstehender Betrag kann nicht größer sein als der tatsächliche ausstehende Betrag {1} in {2},
Rows with Same Account heads will be merged on Ledger,Zeilen mit denselben Konten werden im Hauptbuch zusammengefasst,
Select Warehouses to get Stock for Materials Planning,"Wählen Sie Lager aus, um Bestände für die Materialplanung zu erhalten",
Select an invoice to load summary data,"Wählen Sie eine Rechnung aus, um die Zusammenfassung zu laden",
Serial / Batch Bundle,Serien- / Chargenbündel,
Serial / Batch Bundle Missing,Serien- / Chargenbündel fehlt,
Serial No Range,Seriennummernbereich,
Serial and Batch Details,Serien- und Chargendetails,
Sets 'Accepted Warehouse' in each row of the Items table.,Legt in jeder Zeile der Artikeltabelle das Annahmelager fest.,
Sets 'Rejected Warehouse' in each row of the Items table.,Legt in jeder Zeile der Artikeltabelle das „Ausschusslager“ fest.,
Shelf Life in Days,Haltbarkeitsdauer in Tagen,
Show Disabled Warehouses,Deaktivierte Lager anzeigen,
Show GL Balance,Hauptbuchsaldo anzeigen,
Show Pay Button in Purchase Order Portal,Schaltfläche „Bezahlen“ im Bestellportal anzeigen,
Show Taxes as Table in Print,Steuern als Tabelle im Druck anzeigen,
Show net values in opening and closing columns,Nettowerte in Eröffnungs- und Abschlussspalten anzeigen,
Show with upcoming revenue/expense,Mit kommenden Einnahmen/Ausgaben anzeigen,
Something went wrong please try again,"Etwas ist schief gelaufen, bitte versuchen Sie es erneut",
South Africa VAT Account,Südafrika Mehrwertsteuer-Konto,
South Africa VAT Settings,Südafrika Mehrwertsteuer-Einstellungen,
Start Date should be lower than End Date,Das Startdatum muss vor dem Enddatum liegen,
Start Deletion,Löschen starten,
Start Time can't be greater than or equal to End Time for {0}.,Die Startzeit kann nicht größer oder gleich der Endzeit für {0} sein.,
Started a background job to create {1} {0},Hintergrundjob zum Erstellen von {1} {0} gestartet,
Status set to rejected as there are one or more rejected readings.,"Der Status wurde auf abgelehnt gesetzt, da es einen oder mehrere abgelehnte Messwerte gibt.",
Stock Consumed During Repair,Während der Reparatur verbrauchter Bestand,
Stock Consumption Details,Details zum Lagerverbrauch,
Stock Planning,Bestandsplanung,
Stock Reservation,Bestandsreservierung,
Stock Reservation Entries Cancelled,Bestandsreservierungen storniert,
Stock Reservation Entries Created,Bestandsreservierungen erstellt,
Stock Reservation Entry,Bestandsreservierungseintrag,
Stock Reservation Entry cannot be updated as it has been delivered.,"Der Bestandsreservierungseintrag kann nicht aktualisiert werden, da er bereits geliefert wurde.",
"Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one.","Ein anhand einer Kommissionierliste erstellter Bestandsreservierungseintrag kann nicht aktualisiert werden. Wenn Sie Änderungen vornehmen müssen, empfehlen wir, den vorhandenen Eintrag zu stornieren und einen neuen zu erstellen.",
Stock Reservation can only be created against {0}.,Bestandsreservierungen können nur gegen {0} erstellt werden.,
Stock Reserved Qty (in Stock UOM),Reservierter Bestand (in Lager-ME),
Stock Unreservation,Aufhebung der Bestandsreservierung,
Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.,"Lagerbestände/Konten können nicht eingefroren werden, da die Verarbeitung rückwirkender Einträge noch läuft. Bitte versuchen Sie es später erneut.",
Supplied Item,Gelieferter Artikel,
Supplies subject to the reverse charge provision,"Lieferungen, die der Reverse-Charge-Regelung unterliegen",
Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.,Aufgabe {0} hängt von Aufgabe {1} ab. Bitte fügen Sie Aufgabe {1} zur Aufgabenliste hinzu.,
Tax Amount will be rounded on a row(items) level,Der Steuerbetrag wird auf (Artikel-)Zeilenebene gerundet,
Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme,Steuererstattungen für Touristen im Rahmen der Steuererstattungsregelung für Touristen,
"Tax detail table fetched from item master as a string and stored in this field.
Used for Taxes and Charges","Steuerdetailtabelle, die aus dem Artikelstamm als Zeichenfolge abgerufen und in diesem Feld gespeichert wird.
Wird für Steuern und Gebühren verwendet",
"The Payment Request {0} is already paid, cannot process payment twice","Die Auszahlungsanforderung {0} ist bereits bezahlt, die Zahlung kann nicht zweimal verarbeitet werden",
The Serial No at Row #{0}: {1} is not available in warehouse {2}.,Die Seriennummer in Zeile #{0}: {1} ist im Lager {2} nicht verfügbar.,
The Work Order is mandatory for Disassembly Order,Der Arbeitsauftrag ist obligatorisch für den Demontageauftrag,
The allocated amount is greater than the outstanding amount of Payment Request {0},Der zugewiesene Betrag ist größer als der ausstehende Betrag der Zahlungsanforderung {0},
The field {0} in row {1} is not set,Das Feld {0} in der Zeile {1} ist nicht gesetzt,
The following invalid Pricing Rules are deleted:,Die folgenden ungültigen Preisregeln werden gelöscht:,
The original invoice should be consolidated before or along with the return invoice.,Die Originalrechnung sollte vor oder zusammen mit der Erstattungsrechnung konsolidiert werden.,
"The sync has started in the background, please check the {0} list for new records.",Die Synchronisierung wurde im Hintergrund gestartet. Bitte überprüfen Sie die Liste {0} auf neue Datensätze.,
"The users with this Role are allowed to create/modify a stock transaction, even though the transaction is frozen.","Die Benutzer mit dieser Rolle dürfen eine Lagerbewegungen erstellen/ändern, auch wenn die Transaktion eingefroren ist.",
There are no active Fiscal Years for which Demo Data can be generated.,"Es gibt keine aktiven Geschäftsjahre, für die Demodaten erstellt werden können.",
There were issues unlinking payment entry {0}.,Es gab Probleme bei der Aufhebung der Verknüpfung der Zahlung {0}.,
"This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox.","Diese Option ist standardmäßig aktiviert. Wenn Sie Materialien für Unterbaugruppen des Artikels, den Sie herstellen, planen möchten, lassen Sie diese Option aktiviert. Wenn Sie die Unterbaugruppen separat planen und herstellen, können Sie dieses Kontrollkästchen deaktivieren.",
To Date is mandatory,Bis Datum ist obligatorisch,
To Delivery Date,Bis Liefertermin,
To Due Date,Bis Fälligkeitsdatum,
To Reference Date,Bis Stichtag,
To cancel a {} you need to cancel the POS Closing Entry {}.,"Um einen {} zu stornieren, müssen Sie die POS-Abschlussbuchung {} stornieren.",
Total Incoming Value (Receipt),Gesamter eingehender Wert (Empfang),
Total Number of Booked Depreciations ,Gesamtzahl der gebuchten Abschreibungen ,
Total Operation Time,Gesamtbetriebszeit,
Total Other Charges,Sonstige Kosten insgesamt,
Total Purchase Amount,Gesamtkaufbetrag,
Total Purchase Cost has been updated,Die Gesamteinkaufskosten wurden aktualisiert,
UnReconcile,Zuordnung aufheben,
Unrealized Profit / Loss account for intra-company transfers,Konto für nicht realisierte Gewinne/Verluste aus konzerninternen Transfers,
Unrealized Profit/Loss account for intra-company transfers,Konto für nicht realisierte Gewinne/Verluste aus konzerninternen Transfers,
Validate Components Quantities Per BOM,Anzahl der Komponenten pro Stückliste überprüfen,
Validate Pricing Rule,Preisregel validieren,
Validate Stock on Save,Lagerbestand beim Speichern validieren,
Warning on Negative Stock,Warnung vor negativem Bestand,
You cannot create a {0} within the closed Accounting Period {1},Sie können innerhalb der abgeschlossenen Abrechnungsperiode {1} kein(e) {0} erstellen,
dated {0},von {0},
must be between 0 and 100,muss zwischen 0 und 100 liegen,
or its descendants,oder seine Nachkommen,
subscription is already cancelled.,abonnement ist bereits storniert.,
{0} Account not found against Customer {1}.,{0} Konto für Kunde {1} nicht gefunden.,
{0} Transaction(s) Reconciled,{0} Transaktion(en) Abgestimmt,
{0} cannot be zero,{0} kann nicht Null sein,
{0} is already running for {1},{0} läuft bereits für {1},
{0} units of Item {1} is not available in any of the warehouses.,{0} Einheiten des Artikels {1} sind in keinem der Lager verfügbar.,
Can't render this file because it is too large.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff