mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-29 23:43:40 +00:00
Compare commits
118 Commits
print-form
...
coderabbit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7ae191478 | ||
|
|
6cacead726 | ||
|
|
2d8513de4e | ||
|
|
992027fe89 | ||
|
|
4eb045d927 | ||
|
|
1717a7c983 | ||
|
|
2de4b2ea56 | ||
|
|
b08e0014f7 | ||
|
|
f912c8419a | ||
|
|
f757adc7f7 | ||
|
|
df65fbbc4a | ||
|
|
59bd35c64d | ||
|
|
bd03bcdcb2 | ||
|
|
7fcf277055 | ||
|
|
1e2bcde0f5 | ||
|
|
f23d6911f3 | ||
|
|
12c1b8a910 | ||
|
|
f4c37f1f20 | ||
|
|
a799af7f9f | ||
|
|
f697679b37 | ||
|
|
f14b3ed723 | ||
|
|
fab7f9ee53 | ||
|
|
6e07aac5b7 | ||
|
|
a4fe0fb809 | ||
|
|
533257c4f3 | ||
|
|
33110951b3 | ||
|
|
12ebab1657 | ||
|
|
50eb6786bf | ||
|
|
590207419a | ||
|
|
92f69ae484 | ||
|
|
610dcbb974 | ||
|
|
e3ca318e93 | ||
|
|
98838b1dd5 | ||
|
|
abc7bf2fd6 | ||
|
|
8a19dc4a20 | ||
|
|
301b294da9 | ||
|
|
bf6c331ac4 | ||
|
|
780d3f5ba4 | ||
|
|
dbf9faa87c | ||
|
|
6494fc42c6 | ||
|
|
3abdfcb269 | ||
|
|
0d58dfd0fa | ||
|
|
f6ebf2d0b3 | ||
|
|
842a3645dc | ||
|
|
a6d92e5ec7 | ||
|
|
ce19514a2c | ||
|
|
e223731924 | ||
|
|
39b6aab714 | ||
|
|
6703610596 | ||
|
|
f4f2d11fa4 | ||
|
|
5c4f778223 | ||
|
|
5f97bec2b3 | ||
|
|
f1a2e1b725 | ||
|
|
17397ae652 | ||
|
|
6b83309750 | ||
|
|
e08f82909c | ||
|
|
4cc2afbd83 | ||
|
|
c780796284 | ||
|
|
ddf4a83cf8 | ||
|
|
b5c739d1cc | ||
|
|
2bc19783cb | ||
|
|
1adbf90d8c | ||
|
|
7386270fce | ||
|
|
156dda8157 | ||
|
|
af974fbccd | ||
|
|
e85238383f | ||
|
|
b380b60486 | ||
|
|
7fa800b874 | ||
|
|
8b6e58d02a | ||
|
|
58203a89f1 | ||
|
|
eb5899c786 | ||
|
|
4605051903 | ||
|
|
15397b17f3 | ||
|
|
b63566681b | ||
|
|
a5e49ea8a1 | ||
|
|
98f186b0e0 | ||
|
|
eac6e6a7dd | ||
|
|
2de3f63478 | ||
|
|
334bb609f0 | ||
|
|
b4cf6a1fb9 | ||
|
|
4a26810871 | ||
|
|
96cd8cdb38 | ||
|
|
912ffc2d64 | ||
|
|
f1f61ff61b | ||
|
|
aaca906a0f | ||
|
|
94b75e80b9 | ||
|
|
67f7341721 | ||
|
|
b11d064a2a | ||
|
|
959c311795 | ||
|
|
1d57bbca11 | ||
|
|
c9d98eb4f0 | ||
|
|
fc7a33ebf8 | ||
|
|
b48bff2029 | ||
|
|
a82c0c20f0 | ||
|
|
83d575206b | ||
|
|
e2d4ce74d9 | ||
|
|
13ce7279a8 | ||
|
|
4672c2c383 | ||
|
|
946073cfd9 | ||
|
|
1d97b7cc2b | ||
|
|
128e243945 | ||
|
|
a6a04e8245 | ||
|
|
6c8e909599 | ||
|
|
aab6271b14 | ||
|
|
ff2faf36a7 | ||
|
|
231356a005 | ||
|
|
630d873214 | ||
|
|
6cc421eec6 | ||
|
|
5d0958c5b1 | ||
|
|
1d7a8dda26 | ||
|
|
4a4c2188ec | ||
|
|
f00a63b69d | ||
|
|
0678638106 | ||
|
|
d610d1dccd | ||
|
|
dbab718aaa | ||
|
|
3578ee1195 | ||
|
|
6730960f56 | ||
|
|
4b7cb6bfad |
@@ -46,7 +46,8 @@ def validate_service_stop_date(doc):
|
||||
if (
|
||||
old_stop_dates
|
||||
and old_stop_dates.get(item.name)
|
||||
and item.service_stop_date != old_stop_dates.get(item.name)
|
||||
and item.service_stop_date
|
||||
and getdate(item.service_stop_date) != getdate(old_stop_dates.get(item.name))
|
||||
):
|
||||
frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx))
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-07-29 11:37:42.678556",
|
||||
"modified": "2025-10-13 15:11:58.300836",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Advance Payment Ledger Entry",
|
||||
|
||||
@@ -34,3 +34,15 @@ class AdvancePaymentLedgerEntry(Document):
|
||||
and not frappe.flags.is_reverse_depr_entry
|
||||
):
|
||||
update_voucher_outstanding(self.against_voucher_type, self.against_voucher_no, None, None, None)
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index(
|
||||
"Advance Payment Ledger Entry",
|
||||
["against_voucher_type", "against_voucher_no"],
|
||||
)
|
||||
|
||||
frappe.db.add_index(
|
||||
"Advance Payment Ledger Entry",
|
||||
["voucher_type", "voucher_no"],
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h2 class="text-center">{{ _("STATEMENT OF ACCOUNTS") }}</h2>
|
||||
<h2 class="text-center">{{ _("GENERAL LEDGER") }}</h2>
|
||||
<div>
|
||||
{% if filters.party[0] == filters.party_name[0] %}
|
||||
<h5 style="float: left;">{{ _("Customer: ") }} <b>{{ filters.party_name[0] }}</b></h5>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2020-05-22 16:46:18.712954",
|
||||
"doctype": "DocType",
|
||||
@@ -69,7 +70,7 @@
|
||||
"fieldname": "frequency",
|
||||
"fieldtype": "Select",
|
||||
"label": "Frequency",
|
||||
"options": "Weekly\nMonthly\nQuarterly"
|
||||
"options": "Daily\nWeekly\nBiweekly\nMonthly\nQuarterly"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
@@ -416,7 +417,7 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2025-09-03 14:24:43.608565",
|
||||
"modified": "2025-10-07 12:19:20.719898",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts",
|
||||
|
||||
@@ -8,7 +8,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, add_months, format_date, getdate, today
|
||||
from frappe.utils import add_days, add_months, add_to_date, format_date, getdate, today
|
||||
from frappe.utils.jinja import validate_template
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.www.printview import get_print_style
|
||||
@@ -55,7 +55,7 @@ class ProcessStatementOfAccounts(Document):
|
||||
enable_auto_email: DF.Check
|
||||
filter_duration: DF.Int
|
||||
finance_book: DF.Link | None
|
||||
frequency: DF.Literal["Weekly", "Monthly", "Quarterly"]
|
||||
frequency: DF.Literal["Daily", "Weekly", "Biweekly", "Monthly", "Quarterly"]
|
||||
from_date: DF.Date | None
|
||||
ignore_cr_dr_notes: DF.Check
|
||||
ignore_exchange_rate_revaluation_journals: DF.Check
|
||||
@@ -555,8 +555,9 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
|
||||
|
||||
if doc.enable_auto_email and from_scheduler:
|
||||
new_to_date = getdate(posting_date or today())
|
||||
if doc.frequency == "Weekly":
|
||||
new_to_date = add_days(new_to_date, 7)
|
||||
if doc.frequency in ("Daily", "Weekly", "Biweekly"):
|
||||
frequency = {"Daily": 1, "Weekly": 7, "Biweekly": 14}
|
||||
new_to_date = add_days(new_to_date, frequency[doc.frequency])
|
||||
else:
|
||||
new_to_date = add_months(new_to_date, 1 if doc.frequency == "Monthly" else 3)
|
||||
new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-center" style="margin-top:0">{{ _(report.report_name) }}</h2>
|
||||
<h2 class="text-center" style="margin-top:0">{{ _("STATEMENT OF ACCOUNTS") }}</h2>
|
||||
<h4 class="text-center">
|
||||
{{ filters.customer_name }}
|
||||
</h4>
|
||||
|
||||
@@ -2629,6 +2629,38 @@ class TestPurchaseInvoice(IntegrationTestCase, StockTestMixin):
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
|
||||
|
||||
@IntegrationTestCase.change_settings(
|
||||
"Buying Settings", {"maintain_same_rate": 0, "set_landed_cost_based_on_purchase_invoice_rate": 1}
|
||||
)
|
||||
def test_pr_status_rate_adjusted_from_pi(self):
|
||||
pr = make_purchase_receipt(qty=5, rate=100)
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
pi.submit()
|
||||
pr.reload()
|
||||
|
||||
# Inital check
|
||||
self.assertEqual(pr.status, "Completed")
|
||||
|
||||
pi.reload()
|
||||
pi.cancel()
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
pi.items[0].rate = 80
|
||||
pi.submit()
|
||||
pr.reload()
|
||||
|
||||
# Test 1 : Adjustment amount is negative
|
||||
self.assertEqual(pr.status, "Completed")
|
||||
|
||||
pi.reload()
|
||||
pi.cancel()
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
pi.items[0].rate = 120
|
||||
pi.submit()
|
||||
pr.reload()
|
||||
|
||||
# Test 2 : Adjustment amount is positive
|
||||
self.assertEqual(pr.status, "Completed")
|
||||
|
||||
def test_opening_invoice_rounding_adjustment_validation(self):
|
||||
pi = make_purchase_invoice(do_not_save=1)
|
||||
pi.items[0].rate = 99.98
|
||||
|
||||
@@ -232,7 +232,7 @@ class TestRepostAccountingLedger(AccountsTestMixin, IntegrationTestCase):
|
||||
company.save()
|
||||
|
||||
test_cc = company.cost_center
|
||||
default_expense_account = company.default_expense_account
|
||||
default_expense_account = company.service_expense_account
|
||||
|
||||
item = make_item(properties={"is_stock_item": 0})
|
||||
|
||||
|
||||
@@ -282,22 +282,40 @@ class SalesInvoice(SellingController):
|
||||
def before_print(self, settings=None):
|
||||
from frappe.contacts.doctype.address.address import get_address_display_list
|
||||
|
||||
super().before_print(settings)
|
||||
|
||||
company_details = frappe.get_value(
|
||||
"Company", self.company, ["company_logo", "website", "phone_no", "email"], as_dict=True
|
||||
)
|
||||
|
||||
address_display_list = get_address_display_list("Company", self.company)
|
||||
address_line = address_display_list[0] if address_display_list else ""
|
||||
|
||||
required_fields = [
|
||||
company_details.get("company_logo"),
|
||||
company_details.get("website"),
|
||||
company_details.get("phone_no"),
|
||||
company_details.get("email"),
|
||||
self.company_address,
|
||||
address_line,
|
||||
]
|
||||
|
||||
if not all(required_fields) and not frappe.has_permission("Company", "write", throw=False):
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"Some required Company details are missing. You don't have permission to update them. Please contact your System Manager."
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
if not self.company_address and not frappe.has_permission("Sales Invoice", "write", throw=False):
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"Company Address is missing. You don't have permission to update it. Please contact your System Manager."
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
address_display_list = get_address_display_list("Company", self.company)
|
||||
address_line = address_display_list[0].get("address_line1") if address_display_list else ""
|
||||
|
||||
required_fields.append(self.company_address)
|
||||
required_fields.append(address_line)
|
||||
|
||||
if not all(required_fields):
|
||||
frappe.publish_realtime(
|
||||
"sales_invoice_before_print",
|
||||
@@ -2845,13 +2863,13 @@ def save_company_master_details(name, company, details):
|
||||
details = frappe.parse_json(details)
|
||||
|
||||
if details.get("email"):
|
||||
validate_email_address(details.email, throw=True)
|
||||
validate_email_address(details.get("email"), throw=True)
|
||||
|
||||
company_fields = ["company_logo", "website", "phone_no", "email"]
|
||||
updated_fields = {field: details.get(field) for field in company_fields if details.get(field)}
|
||||
company_fields_to_update = {field: details.get(field) for field in company_fields if details.get(field)}
|
||||
|
||||
if updated_fields:
|
||||
frappe.db.set_value("Company", company, updated_fields)
|
||||
if company_fields_to_update:
|
||||
frappe.db.set_value("Company", company, company_fields_to_update)
|
||||
|
||||
company_address = details.get("company_address")
|
||||
if details.get("address_line1"):
|
||||
@@ -2861,6 +2879,7 @@ def save_company_master_details(name, company, details):
|
||||
"address_title": details.get("address_title"),
|
||||
"address_type": details.get("address_type"),
|
||||
"address_line1": details.get("address_line1"),
|
||||
"address_line2": details.get("address_line2"),
|
||||
"city": details.get("city"),
|
||||
"state": details.get("state"),
|
||||
"pincode": details.get("pincode"),
|
||||
@@ -2869,20 +2888,22 @@ def save_company_master_details(name, company, details):
|
||||
"links": [{"link_doctype": "Company", "link_name": company}],
|
||||
}
|
||||
)
|
||||
address_doc.insert(ignore_permissions=True)
|
||||
address_doc.insert()
|
||||
company_address = address_doc.name
|
||||
|
||||
if company_address:
|
||||
current_display = frappe.db.get_value("Sales Invoice", name, "company_address_display")
|
||||
if not current_display or details.get("address_line1"):
|
||||
frappe.db.set_value(
|
||||
"Sales Invoice",
|
||||
name,
|
||||
{
|
||||
"company_address": company_address,
|
||||
"company_address_display": get_address_display(company_address),
|
||||
},
|
||||
)
|
||||
company_address_display = frappe.db.get_value("Sales Invoice", name, "company_address_display")
|
||||
if not company_address_display or details.get("address_line1"):
|
||||
from frappe.query_builder import DocType
|
||||
|
||||
SalesInvoice = DocType("Sales Invoice")
|
||||
|
||||
(
|
||||
frappe.qb.update(SalesInvoice)
|
||||
.set(SalesInvoice.company_address, company_address)
|
||||
.set(SalesInvoice.company_address_display, get_address_display(company_address))
|
||||
.where(SalesInvoice.name == name)
|
||||
).run()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -16,12 +16,9 @@
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
.logo-container {
|
||||
width: 90px;
|
||||
border: 1px solid #EDEDED;
|
||||
border-radius: 15px;
|
||||
display: block;
|
||||
}
|
||||
.logo-container img {
|
||||
@@ -72,38 +69,37 @@
|
||||
{{ doc.company }}
|
||||
</div>
|
||||
{% if doc.company_address %}
|
||||
{% set company_address_display = frappe.get_doc("Address", doc.company_address) %}
|
||||
{{ company_address_display.address_line1 or "" }}
|
||||
{% if company_address_display.address_line2 %}{{ company_address_display.address_line2 }}{% endif %}<br>
|
||||
{{ company_address_display.city or "" }} {{ company_address_display.state or "" }} {{ company_address_display.pincode or "" }} {{ company_address_display.country or "" }}<br>
|
||||
{% set company_address = frappe.db.get_value("Address", doc.company_address, ["address_line1", "address_line2", "city", "state", "pincode", "country"], as_dict=True) %}
|
||||
|
||||
{{ company_address.get("address_line1") or "" }}<br>
|
||||
{% if company_address.get("address_line2") %}{{ company_address.get("address_line2") }}<br>{% endif %}
|
||||
{{ company_address.get("city") or "" }}, {{ company_address.get("state") or "" }} {{ company_address.get("pincode") or "" }}, {{ company_address.get("country") or "" }}<br>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="invoice-info-cell">
|
||||
{% set website = frappe.db.get_value("Company", doc.company, "website") %}
|
||||
{% set email = frappe.db.get_value("Company", doc.company, "email") %}
|
||||
{% set phone_no = frappe.db.get_value("Company", doc.company, "phone_no") %}
|
||||
{% set company_details = frappe.db.get_value("Company", doc.company, ["website", "email", "phone_no"], as_dict=True) %}
|
||||
|
||||
<div class="invoice-info">
|
||||
<span class="invoice-label">{{ _("Invoice:") }}</span>
|
||||
<span>{{ doc.name }}</span>
|
||||
</div>
|
||||
{% if website %}
|
||||
{% if company_details.website %}
|
||||
<div class="invoice-info">
|
||||
<span class="invoice-label">{{ _("Website:") }}</span>
|
||||
<span>{{ website }}</span>
|
||||
<span>{{ company_details.website }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if email %}
|
||||
{% if company_details.email %}
|
||||
<div class="invoice-info">
|
||||
<span class="invoice-label">{{ _("Email:") }}</span>
|
||||
<span>{{ email }}</span>
|
||||
<span>{{ company_details.email }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if phone_no %}
|
||||
{% if company_details.phone_no %}
|
||||
<div class="invoice-info">
|
||||
<span class="invoice-label">{{ _("Contact:") }}</span>
|
||||
<span>{{ phone_no }}</span>
|
||||
<span>{{ company_details.phone_no }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
@@ -22,15 +22,14 @@
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.logo {
|
||||
.letter-head .logo {
|
||||
width: 90px;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
.letter-head .logo img {
|
||||
border-radius: 15px;
|
||||
border: 1px solid #ededed;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
@@ -83,12 +82,12 @@
|
||||
{% endif %}
|
||||
<div class="company-name">{{ doc.company }}</div>
|
||||
<div class="company-address">
|
||||
{% if doc.company_address %} {% set company_address_display = frappe.get_doc("Address",
|
||||
doc.company_address) %} {{ company_address_display.address_line1 or "" }} {% if
|
||||
company_address_display.address_line2 %}{{ company_address_display.address_line2 }}<br />{%
|
||||
endif %} {{ company_address_display.city or "" }} {{ company_address_display.state or ""
|
||||
}} {{ company_address_display.pincode or "" }} {{ company_address_display.country or ""
|
||||
}}<br />
|
||||
{% if doc.company_address %}
|
||||
{% set company_address = frappe.db.get_value("Address", doc.company_address, ["address_line1", "address_line2", "city", "state", "pincode", "country"], as_dict=True) %}
|
||||
{{ company_address.address_line1 or "" }}<br />
|
||||
{% if company_address.address_line2 %} {{ company_address.address_line2 }}<br /> {% endif %}
|
||||
{{ company_address.city or "" }}, {{ company_address.state or "" }}
|
||||
{{ company_address.pincode or "" }}, {{ company_address.country or ""}}<br />
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
@@ -100,23 +99,24 @@
|
||||
<br />
|
||||
</div>
|
||||
<div style="text-align: left; float: right" class="other-details">
|
||||
{% set website = frappe.db.get_value("Company", doc.company, "website") %} {% set email =
|
||||
frappe.db.get_value("Company", doc.company, "email") %} {% set phone_no =
|
||||
frappe.db.get_value("Company", doc.company, "phone_no") %} {% if website %}
|
||||
<div>
|
||||
<span class="contact-title">{{ _("Website:") }}</span
|
||||
><span class="contact-value">{{ website }}</span>
|
||||
</div>
|
||||
{% endif %} {% if email %}
|
||||
<div>
|
||||
<span class="contact-title">{{ _("Email:") }}</span
|
||||
><span class="contact-value">{{ email }}</span>
|
||||
</div>
|
||||
{% endif %} {% if phone_no %}
|
||||
<div>
|
||||
<span class="contact-title">{{ _("Contact:") }}</span
|
||||
><span class="contact-value">{{ phone_no }}</span>
|
||||
</div>
|
||||
{% set company_details = frappe.db.get_value("Company", doc.company, ["website", "email", "phone_no"], as_dict=True) %}
|
||||
{% if company_details.website %}
|
||||
<div>
|
||||
<span class="contact-title">{{ _("Website:") }}</span
|
||||
><span class="contact-value">{{ company_details.website }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if company_details.email %}
|
||||
<div>
|
||||
<span class="contact-title">{{ _("Email:") }}</span
|
||||
><span class="contact-value">{{ company_details.email }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if company_details.phone_no %}
|
||||
<div>
|
||||
<span class="contact-title">{{ _("Contact:") }}</span
|
||||
><span class="contact-value">{{ company_details.phone_no }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
@@ -1,332 +0,0 @@
|
||||
{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None,
|
||||
print_heading_template=None) -%}
|
||||
{% if letter_head and not no_letterhead %}
|
||||
<div class="letter-head">{{ letter_head }}</div>
|
||||
{% endif %}
|
||||
{% if print_heading_template %}
|
||||
{{ frappe.render_template(print_heading_template, {"doc":doc}) }}
|
||||
{% endif %}
|
||||
{%- endmacro -%}
|
||||
{% for page in layout %}
|
||||
<div class="page-break invoice-page">
|
||||
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
|
||||
{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.letter-head {
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 12px !important;
|
||||
}
|
||||
.invoice-page {
|
||||
font-family: Inter, sans-serif;
|
||||
color: #171717;
|
||||
}
|
||||
|
||||
.print-format-body {
|
||||
padding: 30px 12px 12px 12px !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #7c7c7c !important;
|
||||
}
|
||||
.text-muted {
|
||||
color: #7c7c7c !important;
|
||||
}
|
||||
.text-dark {
|
||||
color: #171717 !important;
|
||||
}
|
||||
.small-text {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.mt-40 {
|
||||
margin-top: 40px;
|
||||
}
|
||||
.mw-400 {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.items-table,
|
||||
.tax-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.items-table td,
|
||||
.tax-table td {
|
||||
border-bottom: 1px solid #ededed;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.items-table thead td,
|
||||
.tax-table thead td {
|
||||
color: #7c7c7c !important;
|
||||
}
|
||||
.items-table thead tr:first-of-type td,
|
||||
.tax-table thead tr:first-of-type td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.items-table thead tr:first-child td:first-child,
|
||||
.tax-table thead tr:first-child td:first-child {
|
||||
border-top-left-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
}
|
||||
.items-table thead tr:first-child td:last-child,
|
||||
.tax-table thead tr:first-child td:last-child {
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
.in-words {
|
||||
color: #171717 !important;
|
||||
max-width: 400px;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.5;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.row-divider {
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
|
||||
.highlight-bg {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
margin-top: 40px;
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.print-format {
|
||||
color: #171717;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 420;
|
||||
line-height: 21px;
|
||||
padding: 0px !important;
|
||||
margin-left: 0mm !important;
|
||||
margin-right: 0mm !important;
|
||||
}
|
||||
|
||||
.letter-head-footer {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.print-format-body td {
|
||||
padding: 8px 8px !important;
|
||||
}
|
||||
|
||||
.tax-table td,
|
||||
.items-table td {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.tax-table thead td,
|
||||
.items-table thead td {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.item-img td {
|
||||
border: none !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="print-format-body">
|
||||
<table class="info-table col-xs-12" style="width: 100%; margin-left: 12px">
|
||||
<tr>
|
||||
<td class="col-xs-6" style="padding: 0 !important">
|
||||
<div class="title col-xs-5 p-0">
|
||||
<div class="info-text">Customer Name:</div>
|
||||
<div class="info-text">Bill to:</div>
|
||||
</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="info-text">{{ doc.customer_name }}</div>
|
||||
<div class="info-text">
|
||||
{% if doc.customer_address %}
|
||||
{% set customer_address = frappe.get_doc("Address", doc.customer_address) %}
|
||||
{{ customer_address.address_line1 or "" }}<br>
|
||||
{% if customer_address.address_line2 %}{{ customer_address.address_line2 }}<br>{% endif %}
|
||||
{{ customer_address.city or "" }} {{ customer_address.state or "" }} {{ customer_address.pincode or "" }} {{ customer_address.country or "" }}<br>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="col-xs-6" style="padding: 0 !important">
|
||||
<div class="title col-xs-5 p-0">
|
||||
<div class="info-text">Invoice Number:</div>
|
||||
</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="info-text">{{ doc.name }}</div>
|
||||
</div>
|
||||
<div class="title col-xs-5 p-0">
|
||||
<div class="info-text">Invoice Date:</div>
|
||||
</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="info-text">{{ frappe.utils.format_date(doc.posting_date) }}</div>
|
||||
</div>
|
||||
<div class="title col-xs-5 p-0">
|
||||
<div class="info-text">Payment Due Date:</div>
|
||||
</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="info-text">{{ frappe.utils.format_date(doc.due_date) }}</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Items Table -->
|
||||
<table class="items-table mt-15">
|
||||
<colgroup>
|
||||
<col style="width: 5%" />
|
||||
<col style="width: 35%" />
|
||||
<col style="width: 10%" />
|
||||
<col style="width: 20%" />
|
||||
<col style="width: 15%" />
|
||||
</colgroup>
|
||||
<thead class="highlight-bg">
|
||||
<tr>
|
||||
<td class="text-center">{{ _("No") }}</td>
|
||||
<td class="text-left">{{ _("Item") }}</td>
|
||||
<td class="text-right">{{ _("Quantity") }}</td>
|
||||
<td class="text-right">{{ _("Rate") }}</td>
|
||||
<td class="text-right">{{ _("Amount") }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in doc.items %}
|
||||
<tr>
|
||||
<td class="text-center">{{ loop.index }}</td>
|
||||
<td class="text-left">
|
||||
<table class="item-img" style="border-collapse: collapse">
|
||||
<tr>
|
||||
{% if item.image %}
|
||||
<td class="no-style" style="border-radius: 6px; vertical-align: middle">
|
||||
<img
|
||||
src="{{ item.image }}"
|
||||
alt="{{ item.item_name }}"
|
||||
style="
|
||||
border-radius: 6px;
|
||||
max-height: 24px;
|
||||
min-height: 24px;
|
||||
max-width: 24px;
|
||||
min-width: 24px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
"
|
||||
/>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td
|
||||
class="no-style"
|
||||
style="vertical-align: middle !important; padding: 0 !important"
|
||||
>
|
||||
{{ item.item_name }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
|
||||
<td class="text-right">{{ item.get_formatted("qty", 0) }} {{ item.uom }}</td>
|
||||
<td class="text-right">{{ item.get_formatted("net_rate", doc) }}</td>
|
||||
<td class="text-right">{{ item.get_formatted("net_amount", doc) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="test" style="float: right; margin-top: 15px; margin-bottom: 5px;">
|
||||
<table style="width: 100%; border-collapse: collapse">
|
||||
<tr class="row-divider">
|
||||
<td class="text-right text-muted" style="padding-right: 30px !important;">{{ _("Sub Total:") }}</td>
|
||||
<td class="text-right">{{ doc.get_formatted("total", doc) }}</td>
|
||||
</tr>
|
||||
|
||||
{%- if doc.apply_discount_on == "Net Total" -%}
|
||||
<tr class="row-divider">
|
||||
<td class="text-right text-muted" style="padding-right: 30px !important;">
|
||||
{{ _("Discount") }} ({{ doc.additional_discount_percentage }}%):
|
||||
</td>
|
||||
<td class="text-right">{{ doc.get_formatted("discount_amount", doc) }}</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
{%- for tax in doc.taxes -%}
|
||||
{%- if (tax.tax_amount or print_settings.print_taxes_with_zero_amount) and (not tax.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
|
||||
<tr class="row-divider">
|
||||
<td class="text-right text-muted" style="padding-right: 30px !important;">{{ tax.get_formatted("description") }} ({{ tax.get_formatted("rate") }}%):</td>
|
||||
<td class="text-right">{{ tax.get_formatted("tax_amount") }}</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if doc.apply_discount_on == "Grand Total" -%}
|
||||
<tr class="row-divider">
|
||||
<td class="text-right text-muted" style="padding-right: 30px !important;">
|
||||
{{ _("Discount") }} ({{ doc.additional_discount_percentage }}%):
|
||||
</td>
|
||||
<td class="text-right">{{ doc.get_formatted("discount_amount", doc) }}</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="border-bottom: 1px solid #ededed;">
|
||||
<table class="highlight-bg" style="width: 100%; border-collapse: collapse; border-radius: 12px; margin-bottom: 10px;">
|
||||
<tr>
|
||||
<td class="text-left mw-400">
|
||||
<div class="text-capitalize">
|
||||
<span class="title"> {{ _("In Words: ") }}</span>{{ doc.in_words }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right"><b>{{ _("Grand Total:") }}</b></td>
|
||||
<td class="text-right">
|
||||
<span style="font-weight: 700">
|
||||
{{ doc.get_formatted("grand_total", doc) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Terms -->
|
||||
{% if doc.terms %}
|
||||
<div class="info-card">
|
||||
<div class="title">{{ _("Terms and Conditions") }}</div>
|
||||
{{ doc.terms}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"absolute_value": 0,
|
||||
"align_labels_right": 0,
|
||||
"creation": "2025-09-15 16:31:00.732539",
|
||||
"custom_format": 0,
|
||||
"default_print_language": "en",
|
||||
"disabled": 0,
|
||||
"doc_type": "Sales Invoice",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font_size": 14,
|
||||
"idx": 0,
|
||||
"line_breaks": 0,
|
||||
"margin_bottom": 15.0,
|
||||
"margin_left": 15.0,
|
||||
"margin_right": 15.0,
|
||||
"margin_top": 15.0,
|
||||
"modified": "2025-09-15 16:31:00.732539",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Print Format Sales Invoice",
|
||||
"owner": "Administrator",
|
||||
"page_number": "Hide",
|
||||
"pdf_generator": "wkhtmltopdf",
|
||||
"print_format_builder": 0,
|
||||
"print_format_builder_beta": 0,
|
||||
"print_format_for": "DocType",
|
||||
"print_format_type": "Jinja",
|
||||
"raw_printing": 0,
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None,
|
||||
print_heading_template=None) -%}
|
||||
{% if letter_head and not no_letterhead %}
|
||||
<div class="letter-head">{{ letter_head }}</div>
|
||||
{% endif %}
|
||||
{% if print_heading_template %}
|
||||
{{ frappe.render_template(print_heading_template, {"doc":doc}) }}
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{%- if doc.meta.is_submittable and doc.docstatus==2-%}
|
||||
<div class="text-center" document-status="cancelled">
|
||||
<h4 style="margin: 0px">{{ _("CANCELLED") }}</h4>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- endmacro -%} {% for page in layout %}
|
||||
<div class="page-break invoice-wrapper">
|
||||
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
|
||||
{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.letter-head {
|
||||
margin-top: 12px !important;
|
||||
}
|
||||
.invoice-wrapper {
|
||||
font-family: "Inter", sans-serif;
|
||||
color: var(--black-overlay-700);
|
||||
}
|
||||
|
||||
.print-format-body {
|
||||
padding: 30px 12px 12px 12px !important;
|
||||
}
|
||||
|
||||
body {
|
||||
margin-bottom: 0mm !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
table.info-table,
|
||||
table.items-table,
|
||||
table.tax-table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border: 1px solid #ededed;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.info-table td {
|
||||
border-bottom: 1px solid #ededed;
|
||||
padding: 8px 10px !important;
|
||||
}
|
||||
table.items-table td,
|
||||
table.tax-table td {
|
||||
border-bottom: 1px solid #ededed;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
table.info-table td:not(:first-of-type),
|
||||
table.items-table td:not(:first-of-type),
|
||||
table.tax-table td:not(:first-of-type) {
|
||||
border-left: 1px solid #ededed;
|
||||
}
|
||||
|
||||
table.info-table tbody tr:last-of-type td,
|
||||
table.items-table tbody tr:last-of-type td,
|
||||
table.tax-table tbody tr:last-of-type td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
thead.table-header {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
thead.table-header td {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-muted {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
.text-dark {
|
||||
color: #525252;
|
||||
}
|
||||
.text-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.small-text {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.mt-15 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.mt-20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mt-40 {
|
||||
margin-top: 40px;
|
||||
}
|
||||
.mt-80 {
|
||||
margin-top: 80px;
|
||||
}
|
||||
.mb-20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pb-8 {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.amount-width {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.totals-table {
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.totals-table td {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.totals-table tr {
|
||||
border-bottom: 1px solid #ededed;
|
||||
}
|
||||
.totals-table tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.totals-table .grand-total td {
|
||||
padding: 6px 8px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.words-box {
|
||||
background: #f8f8f8;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
line-height: 21px;
|
||||
letter-spacing: 0.21px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #7c7c7c !important;
|
||||
}
|
||||
|
||||
.heading {
|
||||
color: #525252 !important;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.print-format {
|
||||
color: #171717;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 420;
|
||||
line-height: 21px;
|
||||
padding: 0px;
|
||||
letter-spacing: 0.14px;
|
||||
margin-left: 0mm !important;
|
||||
margin-right: 0mm !important;
|
||||
}
|
||||
.letter-head-footer {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
.print-heading {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
padding-bottom: 0px !important;
|
||||
}
|
||||
</style>
|
||||
<div class="print-format-body">
|
||||
<table class="info-table mb-20" style="width: 100%">
|
||||
<tr>
|
||||
<td style="width: 50%">
|
||||
<span class="heading">{{ _("Customer Name") }}:</span> {{doc.customer_name }}
|
||||
</td>
|
||||
<td style="width: 50%">
|
||||
<span class="heading">{{ _("Payment Due Date") }}:</span> {{
|
||||
frappe.utils.format_date(doc.due_date) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="heading">{{ _("Invoice Number") }}:</span> {{ doc.name }}</td>
|
||||
<td>
|
||||
<span class="heading">{{ _("Invoice Date") }}:</span> {{
|
||||
frappe.utils.format_date(doc.posting_date) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="heading">{{ _("Bill From") }}:</span><br />
|
||||
{% if doc.customer_address %}
|
||||
{% set customer_address = frappe.get_doc("Address", doc.customer_address) %}
|
||||
{{ doc.customer_name }}<br>
|
||||
{{ customer_address.address_line1 or "" }}
|
||||
{% if customer_address.address_line2 %}{{ customer_address.address_line2 }}{% endif %}<br>
|
||||
{{ customer_address.city or "" }} {{ customer_address.state or "" }} {{ customer_address.pincode or "" }} {{ customer_address.country or "" }}<br>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><span class="heading">{{ _("Bill To") }}:</span><br />
|
||||
{% if doc.company_address %}
|
||||
{% set company_address_display = frappe.get_doc("Address", doc.company_address) %}
|
||||
{{ doc.company }}<br>
|
||||
{{ company_address_display.address_line1 or "" }}
|
||||
{% if company_address_display.address_line2 %}{{ company_address_display.address_line2 }}{% endif %}<br>
|
||||
{{ company_address_display.city or "" }} {{ company_address_display.state or "" }} {{ company_address_display.pincode or "" }} {{ company_address_display.country or "" }}<br>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Items Table -->
|
||||
<table class="items-table mt-15" style="width: 100%">
|
||||
<colgroup>
|
||||
<col style="width: 5%" />
|
||||
<col style="width: 40%" />
|
||||
<col style="width: 15%" />
|
||||
<col style="width: 20%" />
|
||||
<col style="width: 20%" />
|
||||
</colgroup>
|
||||
<thead class="table-header">
|
||||
<tr>
|
||||
<td class="text-center">{{ _("No") }}</td>
|
||||
<td class="text-left">{{ _("Item") }}</td>
|
||||
<td class="text-right">{{ _("Quantity") }}</td>
|
||||
<td class="text-right">{{ _("Rate") }}</td>
|
||||
<td class="text-right">{{ _("Amount") }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in doc.items %}
|
||||
<tr>
|
||||
<td class="text-center text-dark">{{ loop.index }}</td>
|
||||
<td>{{ item.item_name }}</td>
|
||||
<td class="text-right text-dark">{{ item.get_formatted("qty", 0) }} {{ item.uom }}</td>
|
||||
<td class="text-right text-dark">{{ item.get_formatted("net_rate", doc) }}</td>
|
||||
<td class="text-right" style="color: #171717">
|
||||
{{ item.get_formatted("net_amount", doc) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Totals Section -->
|
||||
<table style="width:100%; margin-top: 15px">
|
||||
<td style="vertical-align: bottom !important;">
|
||||
<p class="title">{{ _("Total in words") }}</p>
|
||||
<div class="words-box text-uppercase">{{ doc.in_words }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<table class="totals-table">
|
||||
<tr>
|
||||
<td class="text-right text-muted">{{ _("Sub Total:") }}</td>
|
||||
<td class="text-right amount-width">{{ doc.get_formatted("total", doc) }}</td>
|
||||
</tr>
|
||||
{%- if doc.apply_discount_on == "Net Total" -%}
|
||||
<tr>
|
||||
<td class="text-right text-muted">
|
||||
{{ _("Discount") }} ({{ doc.additional_discount_percentage }}%):
|
||||
</td>
|
||||
<td class="text-right amount-width">{{ doc.get_formatted("discount_amount", doc) }}</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
{%- for tax in doc.taxes -%}
|
||||
{%- if (tax.tax_amount or print_settings.print_taxes_with_zero_amount) and (not tax.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
|
||||
<tr>
|
||||
<td class="text-right text-muted">{{ tax.get_formatted("description") }} ({{ tax.get_formatted("rate") }}%):</td>
|
||||
<td class="text-right amount-width">{{ tax.get_formatted("tax_amount") }}</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if doc.apply_discount_on == "Grand Total" -%}
|
||||
<tr>
|
||||
<td class="text-right text-muted">
|
||||
{{ _("Discount") }} ({{ doc.additional_discount_percentage }}%):
|
||||
</td>
|
||||
<td class="text-right amount-width">{{ doc.get_formatted("discount_amount", doc) }}</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
<tr class="grand-total">
|
||||
<td class="text-right">{{ _("Grand Total:") }}</td>
|
||||
<td class="text-right amount-width">{{ doc.get_formatted("grand_total", doc) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</table>
|
||||
|
||||
<!-- Terms -->
|
||||
<div class="terms-section">
|
||||
{% if doc.terms %}
|
||||
<div class="info-card mt-40">
|
||||
<div>{{ _("Terms and Conditions") }}</div>
|
||||
{{ doc.terms}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"absolute_value": 0,
|
||||
"align_labels_right": 0,
|
||||
"creation": "2025-08-28 04:03:36.284420",
|
||||
"custom_format": 0,
|
||||
"default_print_language": "en",
|
||||
"disabled": 0,
|
||||
"doc_type": "Sales Invoice",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font_size": 14,
|
||||
"idx": 0,
|
||||
"line_breaks": 0,
|
||||
"margin_bottom": 15.0,
|
||||
"margin_left": 15.0,
|
||||
"margin_right": 15.0,
|
||||
"margin_top": 15.0,
|
||||
"modified": "2025-09-01 17:47:14.710435",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Print Format",
|
||||
"owner": "Administrator",
|
||||
"page_number": "Hide",
|
||||
"pdf_generator": "wkhtmltopdf",
|
||||
"print_format_builder": 0,
|
||||
"print_format_builder_beta": 0,
|
||||
"print_format_for": "DocType",
|
||||
"print_format_type": "Jinja",
|
||||
"raw_printing": 0,
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -354,7 +354,7 @@ def get_asset_details_for_grouped_by_category(filters):
|
||||
# nosemgrep
|
||||
return frappe.db.sql(
|
||||
f"""
|
||||
SELECT a.name,
|
||||
SELECT a.name, a.asset_name,
|
||||
ifnull(sum(case when a.purchase_date < %(from_date)s then
|
||||
case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then
|
||||
a.net_purchase_amount
|
||||
@@ -583,6 +583,14 @@ def get_columns(filters):
|
||||
"width": 120,
|
||||
}
|
||||
)
|
||||
columns.append(
|
||||
{
|
||||
"label": _("Asset Name"),
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Data",
|
||||
"width": 140,
|
||||
}
|
||||
)
|
||||
|
||||
columns += [
|
||||
{
|
||||
|
||||
@@ -319,6 +319,7 @@ def get_asset_value_adjustment_map(filters, finance_book):
|
||||
.select(asset.name.as_("asset"), Sum(gle.debit - gle.credit).as_("adjustment_amount"))
|
||||
.where(gle.account == aca.fixed_asset_account)
|
||||
.where(gle.is_cancelled == 0)
|
||||
.where(gle.is_opening == "No")
|
||||
.where(company.name == filters.company)
|
||||
.where(asset.docstatus == 1)
|
||||
)
|
||||
|
||||
@@ -284,15 +284,15 @@ def get_columns(filters):
|
||||
|
||||
|
||||
def get_message():
|
||||
return """<span class="indicator">
|
||||
Valid till :
|
||||
return f"""<span class="indicator">
|
||||
{_("Valid Till")}:
|
||||
</span>
|
||||
<span class="indicator orange">
|
||||
Expires in a week or less
|
||||
{_("Expires in a week or less")}
|
||||
</span>
|
||||
|
||||
<span class="indicator red">
|
||||
Expires today / Already Expired
|
||||
{_("Expires today or already expired")}
|
||||
</span>"""
|
||||
|
||||
|
||||
|
||||
@@ -854,13 +854,14 @@ def available_serial_batch_for_return(field, doctype, reference_ids, is_rejected
|
||||
|
||||
def get_available_serial_batches(field, doctype, reference_ids, is_rejected=False):
|
||||
_bundle_ids = get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=is_rejected)
|
||||
|
||||
if not _bundle_ids:
|
||||
return frappe._dict({})
|
||||
|
||||
return get_serial_batches_based_on_bundle(field, _bundle_ids)
|
||||
return get_serial_batches_based_on_bundle(doctype, field, _bundle_ids)
|
||||
|
||||
|
||||
def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
||||
def get_serial_batches_based_on_bundle(doctype, field, _bundle_ids):
|
||||
available_dict = frappe._dict({})
|
||||
batch_serial_nos = frappe.get_all(
|
||||
"Serial and Batch Bundle",
|
||||
@@ -872,6 +873,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
||||
"`tabSerial and Batch Bundle`.`voucher_detail_no`",
|
||||
"`tabSerial and Batch Bundle`.`voucher_type`",
|
||||
"`tabSerial and Batch Bundle`.`voucher_no`",
|
||||
"`tabSerial and Batch Bundle`.`item_code`",
|
||||
],
|
||||
filters=[
|
||||
["Serial and Batch Bundle", "name", "in", _bundle_ids],
|
||||
@@ -885,6 +887,16 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
||||
if frappe.get_cached_value(row.voucher_type, row.voucher_no, "is_return"):
|
||||
key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field)
|
||||
|
||||
if doctype == "Packed Item":
|
||||
if key is None:
|
||||
key = frappe.get_cached_value("Packed Item", row.voucher_detail_no, field)
|
||||
if row.voucher_type == "Delivery Note":
|
||||
key = frappe.get_cached_value("Delivery Note Item", key, "dn_detail")
|
||||
elif row.voucher_type == "Sales Invoice":
|
||||
key = frappe.get_cached_value("Sales Invoice Item", key, "sales_invoice_item")
|
||||
|
||||
key = (row.item_code, key)
|
||||
|
||||
if row.voucher_type in ["Sales Invoice", "Delivery Note"]:
|
||||
row.qty = -1 * row.qty
|
||||
|
||||
@@ -913,6 +925,8 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
||||
|
||||
def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False):
|
||||
filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")}
|
||||
if doctype == "Packed Item":
|
||||
filters = get_filters_for_packed_item(field, reference_ids)
|
||||
|
||||
pluck_field = "serial_and_batch_bundle"
|
||||
if is_rejected:
|
||||
@@ -926,10 +940,14 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False
|
||||
pluck=pluck_field,
|
||||
)
|
||||
|
||||
if _bundle_ids and doctype == "Packed Item":
|
||||
return _bundle_ids
|
||||
|
||||
if not _bundle_ids:
|
||||
return {}
|
||||
|
||||
del filters["name"]
|
||||
if "name" in filters:
|
||||
del filters["name"]
|
||||
|
||||
filters[field] = ("in", reference_ids)
|
||||
|
||||
@@ -972,10 +990,29 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False
|
||||
return _bundle_ids
|
||||
|
||||
|
||||
def get_filters_for_packed_item(field, reference_ids):
|
||||
names = []
|
||||
filters = {"docstatus": 1, "dn_detail": ("in", reference_ids)}
|
||||
if dns := frappe.get_all("Delivery Note Item", filters=filters, pluck="name"):
|
||||
names.extend(dns)
|
||||
|
||||
filters = {"docstatus": 1, "sales_invoice_item": ("in", reference_ids)}
|
||||
if sis := frappe.get_all("Sales Invoice Item", filters=filters, pluck="name"):
|
||||
names.extend(sis)
|
||||
|
||||
if names:
|
||||
reference_ids.extend(names)
|
||||
|
||||
return {"docstatus": 1, field: ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")}
|
||||
|
||||
|
||||
def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None):
|
||||
if not qty_field:
|
||||
qty_field = "stock_qty"
|
||||
|
||||
if not hasattr(row, qty_field):
|
||||
qty_field = "qty"
|
||||
|
||||
if not warehouse_field:
|
||||
warehouse_field = "warehouse"
|
||||
|
||||
@@ -1065,6 +1102,9 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f
|
||||
if not qty_field:
|
||||
qty_field = "stock_qty"
|
||||
|
||||
if not hasattr(child_doc, qty_field):
|
||||
qty_field = "qty"
|
||||
|
||||
warehouse = child_doc.get(warehouse_field)
|
||||
if parent_doc.get("is_internal_customer"):
|
||||
warehouse = child_doc.get("target_warehouse")
|
||||
|
||||
@@ -519,8 +519,15 @@ class SellingController(StockController):
|
||||
if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||
continue
|
||||
|
||||
item_details = frappe.get_cached_value(
|
||||
"Item", d.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
|
||||
)
|
||||
|
||||
if not self.get("return_against") or (
|
||||
get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return")
|
||||
get_valuation_method(d.item_code) == "Moving Average"
|
||||
and self.get("is_return")
|
||||
and not item_details.has_serial_no
|
||||
and not item_details.has_batch_no
|
||||
):
|
||||
# Get incoming rate based on original item cost based on valuation method
|
||||
qty = flt(d.get("stock_qty") or d.get("actual_qty") or d.get("qty"))
|
||||
@@ -999,6 +1006,9 @@ def set_default_income_account_for_item(obj):
|
||||
def get_serial_and_batch_bundle(child, parent, delivery_note_child=None):
|
||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||
|
||||
if parent.get("is_return") and parent.get("packed_items"):
|
||||
return
|
||||
|
||||
if child.get("use_serial_batch_fields"):
|
||||
return
|
||||
|
||||
|
||||
@@ -265,6 +265,8 @@ class StatusUpdater(Document):
|
||||
# if target_ref_field is not specified, the programmer does not want to validate qty / amount
|
||||
continue
|
||||
|
||||
items_to_validate = []
|
||||
|
||||
# get unique transactions to update
|
||||
for d in self.get_all_children():
|
||||
if hasattr(d, "qty") and d.qty < 0 and not self.get("is_return"):
|
||||
@@ -286,31 +288,63 @@ class StatusUpdater(Document):
|
||||
)
|
||||
|
||||
if d.doctype == args["source_dt"] and d.get(args["join_field"]):
|
||||
args["name"] = d.get(args["join_field"])
|
||||
|
||||
is_from_pp = (
|
||||
hasattr(d, "production_plan_sub_assembly_item")
|
||||
and frappe.db.get_value(
|
||||
"Production Plan Sub Assembly Item",
|
||||
d.production_plan_sub_assembly_item,
|
||||
"type_of_manufacturing",
|
||||
items_to_validate.append(
|
||||
frappe._dict(
|
||||
{
|
||||
"name": d.get(args["join_field"]),
|
||||
"production_plan_sub_assembly_item": d.get(
|
||||
"production_plan_sub_assembly_item"
|
||||
),
|
||||
"idx": d.idx,
|
||||
"child_doc": d,
|
||||
}
|
||||
)
|
||||
== "Subcontract"
|
||||
)
|
||||
args["item_code"] = "production_item" if is_from_pp else "item_code"
|
||||
|
||||
# get all qty where qty > target_field
|
||||
item = frappe.db.sql(
|
||||
"""select `{item_code}` as item_code, `{target_ref_field}`,
|
||||
`{target_field}`, parenttype, parent from `tab{target_dt}`
|
||||
where `{target_ref_field}` < `{target_field}`
|
||||
and name=%s and docstatus=1""".format(**args),
|
||||
args["name"],
|
||||
as_dict=1,
|
||||
if items_to_validate:
|
||||
pp_sub_assembly_items = [
|
||||
item.production_plan_sub_assembly_item
|
||||
for item in items_to_validate
|
||||
if item.production_plan_sub_assembly_item
|
||||
]
|
||||
|
||||
pp_subcontract_items = []
|
||||
if pp_sub_assembly_items:
|
||||
pp_subcontract_items = frappe.db.get_all(
|
||||
"Production Plan Sub Assembly Item",
|
||||
filters={
|
||||
"name": ("in", pp_sub_assembly_items),
|
||||
"type_of_manufacturing": "Subcontract",
|
||||
},
|
||||
pluck="name",
|
||||
)
|
||||
|
||||
regular_items = []
|
||||
pp_items = []
|
||||
|
||||
for item in items_to_validate:
|
||||
if item.production_plan_sub_assembly_item in pp_subcontract_items:
|
||||
pp_items.append(item.name)
|
||||
else:
|
||||
regular_items.append(item.name)
|
||||
|
||||
item_details = []
|
||||
|
||||
# Query regular items with item_code field
|
||||
if regular_items:
|
||||
item_details.extend(self.fetch_items_with_pending_qty(args, "item_code", regular_items))
|
||||
|
||||
# Query production plan items with production_item field
|
||||
if pp_items:
|
||||
item_details.extend(self.fetch_items_with_pending_qty(args, "production_item", pp_items))
|
||||
|
||||
item_lookup = {item.name: item for item in item_details}
|
||||
|
||||
for child_item in items_to_validate:
|
||||
item = item_lookup.get(child_item.name)
|
||||
|
||||
if item:
|
||||
item = item[0]
|
||||
item["idx"] = d.idx
|
||||
item["idx"] = child_item.idx
|
||||
item["target_ref_field"] = args["target_ref_field"].replace("_", " ")
|
||||
|
||||
# if not item[args['target_ref_field']]:
|
||||
@@ -323,6 +357,28 @@ class StatusUpdater(Document):
|
||||
elif item[args["target_ref_field"]]:
|
||||
self.check_overflow_with_allowance(item, args)
|
||||
|
||||
def fetch_items_with_pending_qty(self, args, item_field, items):
|
||||
doctype = frappe.qb.DocType(args["target_dt"])
|
||||
item_field = doctype[item_field]
|
||||
target_ref_field = doctype[args["target_ref_field"]]
|
||||
target_field = doctype[args["target_field"]]
|
||||
|
||||
return (
|
||||
frappe.qb.from_(doctype)
|
||||
.select(
|
||||
doctype.name,
|
||||
item_field.as_("item_code"),
|
||||
target_ref_field,
|
||||
target_field,
|
||||
doctype.parenttype,
|
||||
doctype.parent,
|
||||
)
|
||||
.where(target_ref_field < target_field)
|
||||
.where(doctype.name.isin(items))
|
||||
.where(doctype.docstatus == 1)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
def check_overflow_with_allowance(self, item, args):
|
||||
"""
|
||||
Checks if there is overflow condering a relaxation allowance
|
||||
|
||||
@@ -360,10 +360,20 @@ class StockController(AccountsController):
|
||||
return
|
||||
|
||||
child_doctype = self.doctype + " Item"
|
||||
if table_name == "packed_items":
|
||||
field = "parent_detail_docname"
|
||||
child_doctype = "Packed Item"
|
||||
|
||||
available_dict = available_serial_batch_for_return(field, child_doctype, reference_ids)
|
||||
|
||||
for row in self.get(table_name):
|
||||
if data := available_dict.get(row.get(field)):
|
||||
value = row.get(field)
|
||||
if table_name == "packed_items" and row.get("parent_detail_docname"):
|
||||
value = self.get_value_for_packed_item(row)
|
||||
if not value:
|
||||
continue
|
||||
|
||||
if data := available_dict.get(value):
|
||||
data = filter_serial_batches(self, data, row)
|
||||
bundle = make_serial_batch_bundle_for_return(data, row, self)
|
||||
row.db_set(
|
||||
@@ -379,6 +389,14 @@ class StockController(AccountsController):
|
||||
"incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate")
|
||||
)
|
||||
|
||||
def get_value_for_packed_item(self, row):
|
||||
parent_items = self.get("items", {"name": row.parent_detail_docname})
|
||||
if parent_items:
|
||||
ref = parent_items[0].get("dn_detail")
|
||||
return (row.item_code, ref)
|
||||
|
||||
return None
|
||||
|
||||
def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]:
|
||||
field = {
|
||||
"Sales Invoice": "sales_invoice_item",
|
||||
@@ -413,6 +431,12 @@ class StockController(AccountsController):
|
||||
):
|
||||
reference_ids.append(row.get(field))
|
||||
|
||||
if table_name == "packed_items" and row.get("parent_detail_docname"):
|
||||
parent_rows = self.get("items", {"name": row.parent_detail_docname}) or []
|
||||
for d in parent_rows:
|
||||
if d.get(field) and not d.get(bundle_field):
|
||||
reference_ids.append(d.get(field))
|
||||
|
||||
return field, reference_ids
|
||||
|
||||
@frappe.request_cache
|
||||
|
||||
@@ -202,6 +202,9 @@ class SubcontractingController(StockController):
|
||||
self.set(self.raw_material_table, [])
|
||||
return
|
||||
|
||||
if not self.get(self.raw_material_table):
|
||||
return
|
||||
|
||||
item_dict = self.__get_data_before_save()
|
||||
if not item_dict:
|
||||
return True
|
||||
@@ -657,6 +660,9 @@ class SubcontractingController(StockController):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos_for_outward
|
||||
from erpnext.stock.get_item_details import get_filtered_serial_nos
|
||||
|
||||
if self.is_return:
|
||||
return
|
||||
|
||||
for row in self.supplied_items:
|
||||
item_details = frappe.get_cached_value(
|
||||
"Item", row.rm_item_code, ["has_batch_no", "has_serial_no"], as_dict=1
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-10-05 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-10-06 10:13\n"
|
||||
"PO-Revision-Date: 2025-10-09 11:15\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Persian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -4127,7 +4127,7 @@ msgstr "همه این آیتمها قبلاً صورتحساب/بازگردا
|
||||
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:85
|
||||
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:92
|
||||
msgid "Allocate"
|
||||
msgstr "اختصاص دهید"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the allocate_advances_automatically (Check) field in DocType 'POS
|
||||
#. Invoice'
|
||||
@@ -5628,7 +5628,7 @@ msgstr "اسلات رزرو قرار"
|
||||
|
||||
#: erpnext/crm/doctype/appointment/appointment.py:95
|
||||
msgid "Appointment Confirmation"
|
||||
msgstr "تایید قرار ملاقات"
|
||||
msgstr "تأیید قرار ملاقات"
|
||||
|
||||
#: erpnext/www/book_appointment/index.js:237
|
||||
msgid "Appointment Created Successfully"
|
||||
@@ -5661,7 +5661,7 @@ msgstr "ملاقات با"
|
||||
|
||||
#: erpnext/crm/doctype/appointment/appointment.py:101
|
||||
msgid "Appointment was created. But no lead was found. Please check the email to confirm"
|
||||
msgstr "قرار ملاقات ایجاد شد. اما سرنخی پیدا نشد. لطفا برای تایید ایمیل را بررسی کنید"
|
||||
msgstr "قرار ملاقات ایجاد شد. اما سرنخی پیدا نشد. لطفا برای تأیید ایمیل را بررسی کنید"
|
||||
|
||||
#. Label of the approving_role (Link) field in DocType 'Authorization Rule'
|
||||
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
|
||||
@@ -5675,7 +5675,7 @@ msgstr "نقش تأیید نمیتواند با نقشی که قانون بر
|
||||
#. Label of the approving_user (Link) field in DocType 'Authorization Rule'
|
||||
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
|
||||
msgid "Approving User (above authorized value)"
|
||||
msgstr "تایید کاربر (بالاتر از مقدار مجاز)"
|
||||
msgstr "تأیید کاربر (بالاتر از مقدار مجاز)"
|
||||
|
||||
#: erpnext/setup/doctype/authorization_rule/authorization_rule.py:77
|
||||
msgid "Approving User cannot be same as user the rule is Applicable To"
|
||||
@@ -9434,7 +9434,7 @@ msgstr "برنامه های کمپین"
|
||||
|
||||
#: erpnext/setup/doctype/authorization_control/authorization_control.py:60
|
||||
msgid "Can be approved by {0}"
|
||||
msgstr "قابل تایید توسط {0}"
|
||||
msgstr "قابل تأیید توسط {0}"
|
||||
|
||||
#: erpnext/manufacturing/doctype/work_order/work_order.py:2172
|
||||
msgid "Can not close Work Order. Since {0} Job Cards are in Work In Progress state."
|
||||
@@ -10655,7 +10655,7 @@ msgstr "هنگامی که فایل فشرده به سند پیوست شد، رو
|
||||
|
||||
#: erpnext/templates/emails/confirm_appointment.html:3
|
||||
msgid "Click on the link below to verify your email and confirm the appointment"
|
||||
msgstr "برای تایید ایمیل خود و تایید قرار ملاقات روی لینک زیر کلیک کنید"
|
||||
msgstr "برای تأیید ایمیل خود و تأیید قرار ملاقات روی لینک زیر کلیک کنید"
|
||||
|
||||
#: erpnext/selling/page/point_of_sale/pos_item_cart.js:485
|
||||
msgid "Click to add email / phone"
|
||||
@@ -12000,7 +12000,7 @@ msgstr ""
|
||||
#. Label of the final_confirmation_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Confirmation Date"
|
||||
msgstr "تاریخ تایید"
|
||||
msgstr "تاریخ تأیید"
|
||||
|
||||
#. Label of the connections_tab (Tab Break) field in DocType 'Purchase Invoice'
|
||||
#. Label of the connections_tab (Tab Break) field in DocType 'Sales Invoice'
|
||||
@@ -23223,7 +23223,7 @@ msgstr "درصد سود ناخالص"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:171
|
||||
msgid "Gross Profit Ratio"
|
||||
msgstr ""
|
||||
msgstr "نسبت سود ناخالص"
|
||||
|
||||
#. Label of the gross_purchase_amount (Currency) field in DocType 'Asset
|
||||
#. Depreciation Schedule'
|
||||
@@ -24763,7 +24763,7 @@ msgstr "در تولید"
|
||||
#: erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.json
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
msgid "In Progress"
|
||||
msgstr "در حال پیش رفت"
|
||||
msgstr "در حال انجام"
|
||||
|
||||
#: erpnext/stock/report/available_serial_no/available_serial_no.py:112
|
||||
#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:82
|
||||
@@ -26030,7 +26030,7 @@ msgstr "تنظیمات موجودی"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:214
|
||||
msgid "Inventory Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "نسبت گردش موجودی"
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/industry_type.txt:29
|
||||
msgid "Investment Banking"
|
||||
@@ -32558,7 +32558,7 @@ msgstr "سود خالص"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:172
|
||||
msgid "Net Profit Ratio"
|
||||
msgstr ""
|
||||
msgstr "نسبت سود خالص"
|
||||
|
||||
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:180
|
||||
msgid "Net Profit/Loss"
|
||||
@@ -34796,12 +34796,12 @@ msgstr "سفارش توسط"
|
||||
#. Order'
|
||||
#: erpnext/buying/doctype/purchase_order/purchase_order.json
|
||||
msgid "Order Confirmation Date"
|
||||
msgstr "تاریخ تایید سفارش"
|
||||
msgstr "تاریخ تأیید سفارش"
|
||||
|
||||
#. Label of the order_confirmation_no (Data) field in DocType 'Purchase Order'
|
||||
#: erpnext/buying/doctype/purchase_order/purchase_order.json
|
||||
msgid "Order Confirmation No"
|
||||
msgstr "شماره تایید سفارش"
|
||||
msgstr "شماره تأیید سفارش"
|
||||
|
||||
#: erpnext/crm/report/campaign_efficiency/campaign_efficiency.py:23
|
||||
#: erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py:29
|
||||
@@ -38140,7 +38140,7 @@ msgstr "لطفاً شناسه مشتری Plaid و مقادیر مخفی خود
|
||||
#: erpnext/crm/doctype/appointment/appointment.py:98
|
||||
#: erpnext/www/book_appointment/index.js:235
|
||||
msgid "Please check your email to confirm the appointment"
|
||||
msgstr "لطفا ایمیل خود را برای تایید قرار ملاقات بررسی کنید"
|
||||
msgstr "لطفا ایمیل خود را برای تأیید قرار ملاقات بررسی کنید"
|
||||
|
||||
#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:374
|
||||
msgid "Please click on 'Generate Schedule'"
|
||||
@@ -38263,7 +38263,7 @@ msgstr "لطفاً حساب را برای تغییر مبلغ وارد کنید"
|
||||
|
||||
#: erpnext/setup/doctype/authorization_rule/authorization_rule.py:75
|
||||
msgid "Please enter Approving Role or Approving User"
|
||||
msgstr "لطفاً نقش تأیید یا تأیید کاربر را وارد کنید"
|
||||
msgstr "لطفاً نقش تأیید یا کاربر تأیید را وارد کنید"
|
||||
|
||||
#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:945
|
||||
msgid "Please enter Cost Center"
|
||||
@@ -59037,11 +59037,11 @@ msgstr "تا زمان"
|
||||
#. Option for the 'Status' (Select) field in DocType 'Appointment'
|
||||
#: erpnext/crm/doctype/appointment/appointment.json
|
||||
msgid "Unverified"
|
||||
msgstr "تایید نشده"
|
||||
msgstr "تأیید نشده"
|
||||
|
||||
#: erpnext/erpnext_integrations/utils.py:22
|
||||
msgid "Unverified Webhook Data"
|
||||
msgstr "داده های وب هوک تایید نشده"
|
||||
msgstr "داده های وب هوک تأیید نشده"
|
||||
|
||||
#: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js:17
|
||||
msgid "Up"
|
||||
@@ -60173,7 +60173,7 @@ msgstr "تأیید انجام نشد لطفاً پیوند را بررسی کن
|
||||
#. Label of the verified_by (Data) field in DocType 'Quality Inspection'
|
||||
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
|
||||
msgid "Verified By"
|
||||
msgstr "تایید شده توسط"
|
||||
msgstr "تأیید شده توسط"
|
||||
|
||||
#: erpnext/templates/emails/confirm_appointment.html:6
|
||||
#: erpnext/www/book_appointment/verify/index.html:4
|
||||
@@ -62198,7 +62198,7 @@ msgstr "نام شما (الزامی)"
|
||||
|
||||
#: erpnext/www/book_appointment/verify/index.html:11
|
||||
msgid "Your email has been verified and your appointment has been scheduled"
|
||||
msgstr "ایمیل شما تایید شده و قرار ملاقات شما تعیین شده است"
|
||||
msgstr "ایمیل شما تأیید شده و قرار ملاقات شما تعیین شده است"
|
||||
|
||||
#: erpnext/patches/v11_0/add_default_dispatch_notification_template.py:22
|
||||
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:320
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-10-05 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-10-06 10:14\n"
|
||||
"PO-Revision-Date: 2025-10-08 10:44\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: French\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -41601,7 +41601,7 @@ msgstr "Analyse des bons de commande"
|
||||
|
||||
#: erpnext/buying/report/procurement_tracker/procurement_tracker.py:76
|
||||
msgid "Purchase Order Date"
|
||||
msgstr "Date du de la Commande d'Achat"
|
||||
msgstr "Date de la commande d'achat"
|
||||
|
||||
#. Label of the po_detail (Data) field in DocType 'Purchase Invoice Item'
|
||||
#. Label of the purchase_order_item (Data) field in DocType 'Sales Invoice
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
63249
erpnext/locale/my.po
Normal file
63249
erpnext/locale/my.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-10-05 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-10-06 10:13\n"
|
||||
"PO-Revision-Date: 2025-10-12 11:20\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Polish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -2525,7 +2525,7 @@ msgstr "Zajęcia"
|
||||
#: erpnext/projects/doctype/task/task_dashboard.py:8
|
||||
#: erpnext/support/doctype/issue/issue_dashboard.py:5
|
||||
msgid "Activity"
|
||||
msgstr ""
|
||||
msgstr "Aktywność"
|
||||
|
||||
#. Name of a DocType
|
||||
#. Label of a Link in the Projects Workspace
|
||||
@@ -7829,7 +7829,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
|
||||
#: erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
|
||||
msgid "Barcode"
|
||||
msgstr ""
|
||||
msgstr "Kod kreskowy"
|
||||
|
||||
#. Label of the barcode_type (Select) field in DocType 'Item Barcode'
|
||||
#: erpnext/stock/doctype/item_barcode/item_barcode.json
|
||||
@@ -9425,7 +9425,7 @@ msgstr ""
|
||||
#: erpnext/setup/setup_wizard/data/marketing_source.txt:9
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
msgid "Campaign"
|
||||
msgstr ""
|
||||
msgstr "Kampania"
|
||||
|
||||
#. Name of a report
|
||||
#. Label of a Link in the CRM Workspace
|
||||
@@ -10162,7 +10162,7 @@ msgstr ""
|
||||
#. Label of the category (Link) field in DocType 'UOM Conversion Factor'
|
||||
#: erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.json
|
||||
msgid "Category"
|
||||
msgstr ""
|
||||
msgstr "Kategoria"
|
||||
|
||||
#. Label of the category_details_section (Section Break) field in DocType 'Tax
|
||||
#. Withholding Category'
|
||||
@@ -10932,7 +10932,7 @@ msgstr "Poziom kolekcji"
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
#: erpnext/setup/doctype/vehicle/vehicle.json
|
||||
msgid "Color"
|
||||
msgstr ""
|
||||
msgstr "Kolor"
|
||||
|
||||
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:263
|
||||
msgid "Colour"
|
||||
@@ -14219,7 +14219,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/price_list/price_list.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Currency"
|
||||
msgstr ""
|
||||
msgstr "Waluta"
|
||||
|
||||
#. Label of a Link in the Accounting Workspace
|
||||
#. Name of a DocType
|
||||
@@ -14461,7 +14461,7 @@ msgstr "Opieka"
|
||||
#. Exchange Settings'
|
||||
#: erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
|
||||
msgid "Custom"
|
||||
msgstr ""
|
||||
msgstr "Niestandardowy"
|
||||
|
||||
#. Label of the custom_remarks (Check) field in DocType 'Payment Entry'
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.json
|
||||
@@ -18261,7 +18261,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json
|
||||
#: erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
|
||||
msgid "DocType"
|
||||
msgstr ""
|
||||
msgstr "DocType"
|
||||
|
||||
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:77
|
||||
msgid "DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."
|
||||
@@ -19057,7 +19057,7 @@ msgstr ""
|
||||
#: erpnext/selling/page/point_of_sale/pos_item_cart.js:939
|
||||
#: erpnext/setup/doctype/company/company.json
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
msgstr "E-mail"
|
||||
|
||||
#. Label of a Card Break in the Settings Workspace
|
||||
#: erpnext/setup/workspace/settings/settings.json
|
||||
@@ -19071,7 +19071,7 @@ msgstr ""
|
||||
#: erpnext/setup/workspace/settings/settings.json
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
msgid "Email Account"
|
||||
msgstr ""
|
||||
msgstr "Konto e-mail"
|
||||
|
||||
#. Label of the email_id (Data) field in DocType 'Warehouse'
|
||||
#. Label of a field in the addresses Web Form
|
||||
@@ -20838,7 +20838,7 @@ msgstr "Ujmujący..."
|
||||
#: erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js:106
|
||||
#: erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js:155
|
||||
msgid "Field"
|
||||
msgstr ""
|
||||
msgstr "Pole"
|
||||
|
||||
#. Label of the field_mapping_section (Section Break) field in DocType
|
||||
#. 'Inventory Dimension'
|
||||
@@ -20869,12 +20869,12 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/pos_search_fields/pos_search_fields.json
|
||||
#: erpnext/portal/doctype/website_filter_field/website_filter_field.json
|
||||
msgid "Fieldname"
|
||||
msgstr ""
|
||||
msgstr "Nazwa pola"
|
||||
|
||||
#. Label of the fields (Table) field in DocType 'Item Variant Settings'
|
||||
#: erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
|
||||
msgid "Fields"
|
||||
msgstr ""
|
||||
msgstr "Pola"
|
||||
|
||||
#. Description of the 'Do not update variants on save' (Check) field in DocType
|
||||
#. 'Item Variant Settings'
|
||||
@@ -20894,7 +20894,7 @@ msgstr "Plik to zmiany nazwy"
|
||||
|
||||
#: erpnext/edi/doctype/code_list/code_list_import.js:65
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "Filtr"
|
||||
|
||||
#: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:16
|
||||
#: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:16
|
||||
@@ -23294,7 +23294,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py:17
|
||||
msgid "Group"
|
||||
msgstr ""
|
||||
msgstr "Grupa"
|
||||
|
||||
#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js:30
|
||||
#: erpnext/accounts/report/gross_profit/gross_profit.js:36
|
||||
@@ -23620,7 +23620,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
|
||||
#: erpnext/templates/pages/help.html:3 erpnext/templates/pages/help.html:5
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
msgstr "Pomoc"
|
||||
|
||||
#. Label of the help_section (Tab Break) field in DocType 'Pricing Rule'
|
||||
#: erpnext/accounts/doctype/pricing_rule/pricing_rule.json
|
||||
@@ -23806,7 +23806,7 @@ msgstr ""
|
||||
#. Name of a Workspace
|
||||
#: erpnext/setup/workspace/home/home.json
|
||||
msgid "Home"
|
||||
msgstr ""
|
||||
msgstr "Strona główna"
|
||||
|
||||
#. Name of a UOM
|
||||
#: erpnext/setup/setup_wizard/data/uom_data.json
|
||||
@@ -28854,7 +28854,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/pos_field/pos_field.json
|
||||
#: erpnext/stock/doctype/item_website_specification/item_website_specification.json
|
||||
msgid "Label"
|
||||
msgstr ""
|
||||
msgstr "Etykieta"
|
||||
|
||||
#. Label of the taxes (Table) field in DocType 'Landed Cost Voucher'
|
||||
#: erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
|
||||
@@ -28928,7 +28928,7 @@ msgstr "Krajobraz"
|
||||
#. Label of the language (Link) field in DocType 'Dunning Letter Text'
|
||||
#: erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
msgstr "Język"
|
||||
|
||||
#. Option for the 'Fulfilment Status' (Select) field in DocType 'Contract'
|
||||
#: erpnext/crm/doctype/contract/contract.json
|
||||
@@ -30348,7 +30348,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:254
|
||||
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:101
|
||||
msgid "Mandatory"
|
||||
msgstr ""
|
||||
msgstr "Obowiązkowy"
|
||||
|
||||
#: erpnext/accounts/doctype/pos_profile/pos_profile.py:99
|
||||
msgid "Mandatory Accounting Dimension"
|
||||
@@ -31393,7 +31393,7 @@ msgstr ""
|
||||
#: erpnext/projects/doctype/project/project.json
|
||||
#: erpnext/selling/doctype/sms_center/sms_center.json
|
||||
msgid "Message"
|
||||
msgstr ""
|
||||
msgstr "Wiadomość"
|
||||
|
||||
#. Label of the message_examples (HTML) field in DocType 'Payment Gateway
|
||||
#. Account'
|
||||
@@ -32310,7 +32310,7 @@ msgstr ""
|
||||
#: erpnext/selling/doctype/quotation/quotation.js:274
|
||||
#: erpnext/setup/doctype/employee_group/employee_group.json
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
msgstr "Nazwa"
|
||||
|
||||
#. Label of the name_and_employee_id (Section Break) field in DocType 'Sales
|
||||
#. Person'
|
||||
@@ -32946,7 +32946,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/www/book_appointment/index.html:34
|
||||
msgid "Next"
|
||||
msgstr ""
|
||||
msgstr "Następny"
|
||||
|
||||
#. Label of the next_depreciation_date (Date) field in DocType 'Asset'
|
||||
#: erpnext/assets/doctype/asset/asset.json
|
||||
@@ -33010,7 +33010,7 @@ msgstr "Kolejny e-mali zostanie wysłany w dniu:"
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
#: erpnext/stock/doctype/stock_entry/stock_entry.json
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
msgstr "Nie"
|
||||
|
||||
#: erpnext/setup/doctype/company/test_company.py:99
|
||||
msgid "No Account matched these filters: {}"
|
||||
@@ -33694,7 +33694,7 @@ msgstr ""
|
||||
#. Label of a Link in the Settings Workspace
|
||||
#: erpnext/setup/workspace/settings/settings.json
|
||||
msgid "Notification Settings"
|
||||
msgstr ""
|
||||
msgstr "Ustawienia powiadomień"
|
||||
|
||||
#: erpnext/stock/doctype/delivery_trip/delivery_trip.js:45
|
||||
msgid "Notify Customers via Email"
|
||||
@@ -34530,7 +34530,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/workspace/manufacturing/manufacturing.json
|
||||
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:328
|
||||
msgid "Operation"
|
||||
msgstr ""
|
||||
msgstr "Operacja"
|
||||
|
||||
#. Label of the production_section (Section Break) field in DocType 'Job Card'
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
@@ -34803,7 +34803,7 @@ msgstr ""
|
||||
#. Label of the options (Text) field in DocType 'POS Field'
|
||||
#: erpnext/accounts/doctype/pos_field/pos_field.json
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
msgstr "Opcje"
|
||||
|
||||
#. Option for the 'Color' (Select) field in DocType 'Supplier Scorecard Scoring
|
||||
#. Standing'
|
||||
@@ -36537,7 +36537,7 @@ msgstr ""
|
||||
#: erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json
|
||||
#: erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json
|
||||
msgid "Path"
|
||||
msgstr ""
|
||||
msgstr "Ścieżka"
|
||||
|
||||
#. Option for the 'Status' (Select) field in DocType 'Job Card Operation'
|
||||
#: erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js:96
|
||||
@@ -37687,7 +37687,7 @@ msgstr "Farmaceutyczne"
|
||||
#: erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json
|
||||
#: erpnext/utilities/web_form/addresses/addresses.json
|
||||
msgid "Phone"
|
||||
msgstr ""
|
||||
msgstr "Telefon"
|
||||
|
||||
#. Label of the phone_ext (Data) field in DocType 'Lead'
|
||||
#. Label of the phone_ext (Data) field in DocType 'Opportunity'
|
||||
@@ -40258,7 +40258,7 @@ msgstr ""
|
||||
#. Label of a Card Break in the Settings Workspace
|
||||
#: erpnext/setup/workspace/settings/settings.json
|
||||
msgid "Printing"
|
||||
msgstr ""
|
||||
msgstr "Drukowanie"
|
||||
|
||||
#. Label of the printing_details (Section Break) field in DocType 'Material
|
||||
#. Request'
|
||||
@@ -48391,7 +48391,7 @@ msgstr ""
|
||||
#: erpnext/public/js/call_popup/call_popup.js:169
|
||||
#: erpnext/selling/page/point_of_sale/pos_payment.js:62
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Zapisz"
|
||||
|
||||
#. Option for the 'Action on New Invoice' (Select) field in DocType 'POS
|
||||
#. Profile'
|
||||
@@ -50398,7 +50398,7 @@ msgstr ""
|
||||
#: erpnext/stock/workspace/stock/stock.json
|
||||
#: erpnext/support/workspace/support/support.json
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
msgstr "Ustawienia"
|
||||
|
||||
#. Description of a DocType
|
||||
#: erpnext/crm/doctype/crm_settings/crm_settings.json
|
||||
@@ -51259,7 +51259,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/templates/form_grid/stock_entry_grid.html:29
|
||||
msgid "Source"
|
||||
msgstr ""
|
||||
msgstr "Źródło"
|
||||
|
||||
#. Label of the source_doctype (Link) field in DocType 'Support Search Source'
|
||||
#: erpnext/support/doctype/support_search_source/support_search_source.json
|
||||
@@ -52007,7 +52007,7 @@ msgstr "Stan / prowincja"
|
||||
#: erpnext/templates/pages/task_info.html:69
|
||||
#: erpnext/templates/pages/timelog_info.html:40
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
msgstr "Status"
|
||||
|
||||
#. Label of the status_details (Section Break) field in DocType 'Service Level
|
||||
#. Agreement'
|
||||
@@ -53161,7 +53161,7 @@ msgstr ""
|
||||
#: erpnext/support/web_form/issues/issues.json
|
||||
#: erpnext/templates/pages/task_info.html:44
|
||||
msgid "Subject"
|
||||
msgstr ""
|
||||
msgstr "Temat"
|
||||
|
||||
#: erpnext/accounts/doctype/payment_order/payment_order.js:139
|
||||
#: erpnext/accounts/doctype/pos_invoice/pos_invoice.js:323
|
||||
@@ -54289,7 +54289,7 @@ msgstr ""
|
||||
#: erpnext/utilities/doctype/video/video.json
|
||||
#: erpnext/utilities/doctype/video_settings/video_settings.json
|
||||
msgid "System Manager"
|
||||
msgstr ""
|
||||
msgstr "Menedżer systemu"
|
||||
|
||||
#. Label of a Link in the Settings Workspace
|
||||
#. Label of a shortcut in the Settings Workspace
|
||||
@@ -54384,7 +54384,7 @@ msgstr ""
|
||||
#: erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
|
||||
#: erpnext/templates/form_grid/stock_entry_grid.html:36
|
||||
msgid "Target"
|
||||
msgstr ""
|
||||
msgstr "Cel"
|
||||
|
||||
#. Label of the target_amount (Float) field in DocType 'Target Detail'
|
||||
#: erpnext/setup/doctype/target_detail/target_detail.json
|
||||
@@ -55205,7 +55205,7 @@ msgstr ""
|
||||
#: erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
|
||||
#: erpnext/stock/doctype/item/item_list.js:20
|
||||
msgid "Template"
|
||||
msgstr ""
|
||||
msgstr "Szablon"
|
||||
|
||||
#: erpnext/manufacturing/doctype/bom/bom.js:376
|
||||
msgid "Template Item"
|
||||
@@ -56557,7 +56557,7 @@ msgstr "Szczeliny czasowe"
|
||||
#: erpnext/utilities/doctype/video/video.json
|
||||
#: erpnext/utilities/report/youtube_interactions/youtube_interactions.py:23
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "Tytuł"
|
||||
|
||||
#. Label of the email_to (Data) field in DocType 'Payment Request'
|
||||
#. Label of the to_uom (Link) field in DocType 'UOM Conversion Factor'
|
||||
@@ -57082,7 +57082,7 @@ msgstr "Zbyt wiele kolumn. Wyeksportować raport i wydrukować go za pomocą ark
|
||||
#: erpnext/setup/doctype/email_digest/email_digest.json
|
||||
#: erpnext/stock/workspace/stock/stock.json
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
msgstr "Narzędzia"
|
||||
|
||||
#. Name of a UOM
|
||||
#: erpnext/setup/setup_wizard/data/uom_data.json
|
||||
@@ -58491,7 +58491,7 @@ msgstr ""
|
||||
#: erpnext/stock/report/bom_search/bom_search.py:43
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
msgstr "Typ"
|
||||
|
||||
#. Label of the type_of_call (Link) field in DocType 'Call Log'
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
@@ -58875,7 +58875,7 @@ msgstr ""
|
||||
#: erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js:10
|
||||
#: erpnext/projects/doctype/project/project_dashboard.html:7
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
msgstr "Nieznany"
|
||||
|
||||
#: erpnext/public/js/call_popup/call_popup.js:110
|
||||
msgid "Unknown Caller"
|
||||
@@ -59541,7 +59541,7 @@ msgstr "Używane do Planu Produkcji"
|
||||
#: erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json
|
||||
#: erpnext/utilities/doctype/portal_user/portal_user.json
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
msgstr "Użytkownik"
|
||||
|
||||
#. Label of the section_break_5 (Section Break) field in DocType 'POS Closing
|
||||
#. Entry'
|
||||
@@ -59618,7 +59618,7 @@ msgstr ""
|
||||
#: erpnext/projects/doctype/project/project.json
|
||||
#: erpnext/projects/doctype/project_update/project_update.json
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
msgstr "Użytkownicy"
|
||||
|
||||
#. Description of the 'Track Semi Finished Goods' (Check) field in DocType
|
||||
#. 'BOM'
|
||||
@@ -59963,7 +59963,7 @@ msgstr ""
|
||||
#: erpnext/stock/report/stock_analytics/stock_analytics.js:26
|
||||
#: erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py:101
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
msgstr "Wartość"
|
||||
|
||||
#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js:58
|
||||
msgid "Value (G - D)"
|
||||
@@ -60214,7 +60214,7 @@ msgstr ""
|
||||
#. Label of the version (Data) field in DocType 'Code List'
|
||||
#: erpnext/edi/doctype/code_list/code_list.json
|
||||
msgid "Version"
|
||||
msgstr ""
|
||||
msgstr "Wersja"
|
||||
|
||||
#. Name of a UOM
|
||||
#: erpnext/setup/setup_wizard/data/uom_data.json
|
||||
@@ -61156,7 +61156,7 @@ msgstr ""
|
||||
#: erpnext/setup/workspace/settings/settings.json
|
||||
#: erpnext/stock/doctype/manufacturer/manufacturer.json
|
||||
msgid "Website"
|
||||
msgstr ""
|
||||
msgstr "Strona internetowa"
|
||||
|
||||
#. Name of a DocType
|
||||
#: erpnext/portal/doctype/website_attribute/website_attribute.json
|
||||
@@ -61197,7 +61197,7 @@ msgstr ""
|
||||
#. Label of a Link in the Settings Workspace
|
||||
#: erpnext/setup/workspace/settings/settings.json
|
||||
msgid "Website Settings"
|
||||
msgstr "Ustawienia witryny"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the sb_web_spec (Section Break) field in DocType 'BOM'
|
||||
#: erpnext/manufacturing/doctype/bom/bom.json
|
||||
@@ -62018,7 +62018,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
#: erpnext/stock/doctype/stock_entry/stock_entry.json
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
msgstr "Tak"
|
||||
|
||||
#: erpnext/edi/doctype/code_list/code_list_import.js:29
|
||||
msgid "You are importing data for the code list:"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-10-05 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-10-06 10:13\n"
|
||||
"PO-Revision-Date: 2025-10-08 10:44\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Serbian (Cyrillic)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -14063,7 +14063,7 @@ msgstr "Ограничење потраживања премашено за ку
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:213
|
||||
msgid "Creditor Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Коефицијент обрта добављача"
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:87
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:118
|
||||
@@ -14460,7 +14460,7 @@ msgstr "Тренутна количина"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:152
|
||||
msgid "Current Ratio"
|
||||
msgstr ""
|
||||
msgstr "Рацио опште ликвидности"
|
||||
|
||||
#. Label of the current_serial_and_batch_bundle (Link) field in DocType 'Stock
|
||||
#. Reconciliation Item'
|
||||
@@ -15752,11 +15752,11 @@ msgstr "Дугује-Потражује нису у равнотежи"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:170
|
||||
msgid "Debt Equity Ratio"
|
||||
msgstr ""
|
||||
msgstr "Рацио структуре капитала"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:212
|
||||
msgid "Debtor Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Коефицијент обрта купаца"
|
||||
|
||||
#: erpnext/accounts/party.py:617
|
||||
msgid "Debtor/Creditor"
|
||||
@@ -21451,7 +21451,7 @@ msgstr "Регистар основних средстава"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:211
|
||||
msgid "Fixed Asset Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Коефицијент обрта основних средстава"
|
||||
|
||||
#: erpnext/manufacturing/doctype/bom/bom.py:668
|
||||
msgid "Fixed Asset item {0} cannot be used in BOMs."
|
||||
@@ -23320,7 +23320,7 @@ msgstr "Проценат бруто профита"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:171
|
||||
msgid "Gross Profit Ratio"
|
||||
msgstr ""
|
||||
msgstr "Стопа пословног добитка"
|
||||
|
||||
#. Label of the gross_purchase_amount (Currency) field in DocType 'Asset
|
||||
#. Depreciation Schedule'
|
||||
@@ -26130,7 +26130,7 @@ msgstr "Поставке инвентара"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:214
|
||||
msgid "Inventory Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Коефицијент обрта залиха"
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/industry_type.txt:29
|
||||
msgid "Investment Banking"
|
||||
@@ -29648,7 +29648,7 @@ msgstr "Линк за дневник позива"
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.js:55
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:150
|
||||
msgid "Liquidity Ratios"
|
||||
msgstr ""
|
||||
msgstr "Показатељи ликвидности"
|
||||
|
||||
#. Description of the 'Items' (Section Break) field in DocType 'Product Bundle'
|
||||
#: erpnext/selling/doctype/product_bundle/product_bundle.json
|
||||
@@ -32659,7 +32659,7 @@ msgstr "Нето профит"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:172
|
||||
msgid "Net Profit Ratio"
|
||||
msgstr ""
|
||||
msgstr "Стопа нето добитка"
|
||||
|
||||
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:180
|
||||
msgid "Net Profit/Loss"
|
||||
@@ -42969,7 +42969,7 @@ msgstr "Брзи налог књижења"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:152
|
||||
msgid "Quick Ratio"
|
||||
msgstr ""
|
||||
msgstr "Рацио редуциране ликвидности"
|
||||
|
||||
#. Name of a DocType
|
||||
#. Label of a Link in the Stock Workspace
|
||||
@@ -45949,11 +45949,11 @@ msgstr "Повраћај компоненти"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:173
|
||||
msgid "Return on Asset Ratio"
|
||||
msgstr ""
|
||||
msgstr "Стопа приноса на имовину"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:174
|
||||
msgid "Return on Equity Ratio"
|
||||
msgstr ""
|
||||
msgstr "Стопа приноса на капитал"
|
||||
|
||||
#. Option for the 'Tracking Status' (Select) field in DocType 'Shipment'
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
|
||||
@@ -51297,7 +51297,7 @@ msgstr "Продато од"
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.js:55
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:168
|
||||
msgid "Solvency Ratios"
|
||||
msgstr ""
|
||||
msgstr "Показатељи солвентности"
|
||||
|
||||
#: erpnext/www/book_appointment/index.js:248
|
||||
msgid "Something went wrong please try again"
|
||||
@@ -58524,7 +58524,7 @@ msgstr "Уторак"
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.js:55
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:198
|
||||
msgid "Turnover Ratios"
|
||||
msgstr ""
|
||||
msgstr "Показатељи обртаја"
|
||||
|
||||
#. Option for the 'Frequency To Collect Progress' (Select) field in DocType
|
||||
#. 'Project'
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-10-05 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-10-06 10:13\n"
|
||||
"PO-Revision-Date: 2025-10-08 10:45\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Serbian (Latin)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -14063,7 +14063,7 @@ msgstr "Ograničenje potraživanja premašeno za kupca {0}"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:213
|
||||
msgid "Creditor Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Koeficijent obrta dobavljača"
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:87
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:118
|
||||
@@ -14460,7 +14460,7 @@ msgstr "Trenutna količina"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:152
|
||||
msgid "Current Ratio"
|
||||
msgstr ""
|
||||
msgstr "Racio opšte likvidnosti"
|
||||
|
||||
#. Label of the current_serial_and_batch_bundle (Link) field in DocType 'Stock
|
||||
#. Reconciliation Item'
|
||||
@@ -15752,11 +15752,11 @@ msgstr "Duguje-Potražuje nisu u ravnoteži"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:170
|
||||
msgid "Debt Equity Ratio"
|
||||
msgstr ""
|
||||
msgstr "Racio strukture kapitala"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:212
|
||||
msgid "Debtor Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Koeficijent obrta kupaca"
|
||||
|
||||
#: erpnext/accounts/party.py:617
|
||||
msgid "Debtor/Creditor"
|
||||
@@ -21451,7 +21451,7 @@ msgstr "Registar osnovnih sredstava"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:211
|
||||
msgid "Fixed Asset Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Koeficijent obrta osnovnih sredstava"
|
||||
|
||||
#: erpnext/manufacturing/doctype/bom/bom.py:668
|
||||
msgid "Fixed Asset item {0} cannot be used in BOMs."
|
||||
@@ -23320,7 +23320,7 @@ msgstr "Procenat bruto profita"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:171
|
||||
msgid "Gross Profit Ratio"
|
||||
msgstr ""
|
||||
msgstr "Stopa poslovnog dobitka"
|
||||
|
||||
#. Label of the gross_purchase_amount (Currency) field in DocType 'Asset
|
||||
#. Depreciation Schedule'
|
||||
@@ -26130,7 +26130,7 @@ msgstr "Postavke inventara"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:214
|
||||
msgid "Inventory Turnover Ratio"
|
||||
msgstr ""
|
||||
msgstr "Koeficijent obrta zaliha"
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/industry_type.txt:29
|
||||
msgid "Investment Banking"
|
||||
@@ -29648,7 +29648,7 @@ msgstr "Link za dnevnik poziva"
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.js:55
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:150
|
||||
msgid "Liquidity Ratios"
|
||||
msgstr ""
|
||||
msgstr "Pokazatelji likvidnosti"
|
||||
|
||||
#. Description of the 'Items' (Section Break) field in DocType 'Product Bundle'
|
||||
#: erpnext/selling/doctype/product_bundle/product_bundle.json
|
||||
@@ -32659,7 +32659,7 @@ msgstr "Neto profit"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:172
|
||||
msgid "Net Profit Ratio"
|
||||
msgstr ""
|
||||
msgstr "Stopa neto dobitka"
|
||||
|
||||
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:180
|
||||
msgid "Net Profit/Loss"
|
||||
@@ -42969,7 +42969,7 @@ msgstr "Brzi nalog knjiženja"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:152
|
||||
msgid "Quick Ratio"
|
||||
msgstr ""
|
||||
msgstr "Racio reducirane likvidnosti"
|
||||
|
||||
#. Name of a DocType
|
||||
#. Label of a Link in the Stock Workspace
|
||||
@@ -45949,11 +45949,11 @@ msgstr "Povraćaj komponenti"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:173
|
||||
msgid "Return on Asset Ratio"
|
||||
msgstr ""
|
||||
msgstr "Stopa prinosa na imovinu"
|
||||
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:174
|
||||
msgid "Return on Equity Ratio"
|
||||
msgstr ""
|
||||
msgstr "Stopa prinosa na kapital"
|
||||
|
||||
#. Option for the 'Tracking Status' (Select) field in DocType 'Shipment'
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
|
||||
@@ -51297,7 +51297,7 @@ msgstr "Prodato od"
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.js:55
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:168
|
||||
msgid "Solvency Ratios"
|
||||
msgstr ""
|
||||
msgstr "Pokazatelji solventnosti"
|
||||
|
||||
#: erpnext/www/book_appointment/index.js:248
|
||||
msgid "Something went wrong please try again"
|
||||
@@ -58524,7 +58524,7 @@ msgstr "Utorak"
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.js:55
|
||||
#: erpnext/accounts/report/financial_ratios/financial_ratios.py:198
|
||||
msgid "Turnover Ratios"
|
||||
msgstr ""
|
||||
msgstr "Pokazatelji obrtaja"
|
||||
|
||||
#. Option for the 'Frequency To Collect Progress' (Select) field in DocType
|
||||
#. 'Project'
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-10-05 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-10-06 10:13\n"
|
||||
"PO-Revision-Date: 2025-10-11 11:22\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Swedish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -50930,7 +50930,7 @@ msgstr "Visa Bokföring Register Saldo"
|
||||
|
||||
#: erpnext/accounts/report/trial_balance/trial_balance.js:116
|
||||
msgid "Show Group Accounts"
|
||||
msgstr "Visa Gruppkonton"
|
||||
msgstr "Visa Grupp Konto"
|
||||
|
||||
#. Label of the show_in_website (Check) field in DocType 'Sales Partner'
|
||||
#: erpnext/setup/doctype/sales_partner/sales_partner.json
|
||||
@@ -58449,19 +58449,19 @@ msgstr "Kvalitet Procedur Träd"
|
||||
#: erpnext/accounts/workspace/accounting/accounting.json
|
||||
#: erpnext/accounts/workspace/financial_reports/financial_reports.json
|
||||
msgid "Trial Balance"
|
||||
msgstr "Aktuell Saldo"
|
||||
msgstr "Brutto Saldo"
|
||||
|
||||
#. Name of a report
|
||||
#: erpnext/accounts/report/trial_balance_simple/trial_balance_simple.json
|
||||
msgid "Trial Balance (Simple)"
|
||||
msgstr "Aktuell Saldo (Enkel)"
|
||||
msgstr "Brutto Saldo (Enkel)"
|
||||
|
||||
#. Name of a report
|
||||
#. Label of a Link in the Financial Reports Workspace
|
||||
#: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.json
|
||||
#: erpnext/accounts/workspace/financial_reports/financial_reports.json
|
||||
msgid "Trial Balance for Party"
|
||||
msgstr "Aktuell Saldo för Parti"
|
||||
msgstr "Brutto Saldo för Parti"
|
||||
|
||||
#. Label of the trial_period_end (Date) field in DocType 'Subscription'
|
||||
#: erpnext/accounts/doctype/subscription/subscription.json
|
||||
@@ -58485,7 +58485,7 @@ msgstr "Prov Period Start Datum kan inte vara efter Prenumeration Start Datum"
|
||||
#: erpnext/accounts/doctype/subscription/subscription.json
|
||||
#: erpnext/accounts/doctype/subscription/subscription_list.js:4
|
||||
msgid "Trialing"
|
||||
msgstr "Prövning"
|
||||
msgstr "Prov Period"
|
||||
|
||||
#. Description of the 'General Ledger' (Int) field in DocType 'Accounts
|
||||
#. Settings'
|
||||
@@ -62170,7 +62170,7 @@ msgstr "Du kan inte göra några ändringar i Jobbkort eftersom Arbetsorder är
|
||||
|
||||
#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:187
|
||||
msgid "You can't process the serial number {0} as it has already been used in the SABB {1}. {2} if you want to inward same serial number multiple times then enabled 'Allow existing Serial No to be Manufactured/Received again' in the {3}"
|
||||
msgstr "Du kan inte behandla serienummer {0} eftersom det redan har använts i Serienummer och Parti Paket {1}. {2} Om du vill leverera in samma serienummer flera gånger aktiverar du \"Tillåt att befintligt serienummer produceras/tas emot igen\" i {3}"
|
||||
msgstr "Du kan inte behandla serienummer {0} eftersom det redan har använts i Serienummer och Parti Paket {1}. {2} Om du vill leverera in samma serienummer flera gånger aktiverar du \"Tillåt att befintligt serienummer Produceras/Tas Emot igen\" i {3}"
|
||||
|
||||
#: erpnext/accounts/doctype/loyalty_program/loyalty_program.py:192
|
||||
msgid "You can't redeem Loyalty Points having more value than the Total Amount."
|
||||
|
||||
@@ -1624,6 +1624,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
include_safety_stock = doc.get("include_safety_stock")
|
||||
|
||||
so_item_details = frappe._dict()
|
||||
existing_sub_assembly_items = set()
|
||||
|
||||
sub_assembly_items = defaultdict(int)
|
||||
if doc.get("skip_available_sub_assembly_item") and doc.get("sub_assembly_items"):
|
||||
@@ -1659,7 +1660,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
and doc.get("sub_assembly_items")
|
||||
):
|
||||
item_details = get_raw_materials_of_sub_assembly_items(
|
||||
so_item_details[doc.get("sales_order")].keys() if so_item_details else [],
|
||||
existing_sub_assembly_items,
|
||||
item_details,
|
||||
company,
|
||||
bom_no,
|
||||
@@ -2044,6 +2045,7 @@ def get_raw_materials_of_sub_assembly_items(
|
||||
sub_assembly_items,
|
||||
planned_qty=planned_qty,
|
||||
)
|
||||
existing_sub_assembly_items.add(item.item_code)
|
||||
else:
|
||||
if not item.conversion_factor and item.purchase_uom:
|
||||
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
|
||||
|
||||
@@ -1859,11 +1859,17 @@ class TestProductionPlan(IntegrationTestCase):
|
||||
|
||||
def test_calculation_of_sub_assembly_items(self):
|
||||
make_item("Sub Assembly Item ", properties={"is_stock_item": 1})
|
||||
make_item("Sub Assembly Item 2", properties={"is_stock_item": 1})
|
||||
make_item("RM Item 1", properties={"is_stock_item": 1})
|
||||
make_item("RM Item 2", properties={"is_stock_item": 1})
|
||||
make_item("_Test FG Item 3", properties={"is_stock_item": 1})
|
||||
make_item("_Test FG Item 4", properties={"is_stock_item": 1})
|
||||
make_bom(item="Sub Assembly Item", raw_materials=["RM Item 1", "RM Item 2"])
|
||||
make_bom(item="Sub Assembly Item 2", raw_materials=["RM Item 2"])
|
||||
make_bom(item="_Test FG Item", raw_materials=["Sub Assembly Item", "RM Item 1"])
|
||||
make_bom(item="_Test FG Item 2", raw_materials=["Sub Assembly Item"])
|
||||
make_bom(item="_Test FG Item 3", raw_materials=["RM Item 1"])
|
||||
make_bom(item="_Test FG Item 4", raw_materials=["Sub Assembly Item 2"])
|
||||
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
@@ -1899,13 +1905,40 @@ class TestProductionPlan(IntegrationTestCase):
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
},
|
||||
)
|
||||
# Assembly item with similar RM item
|
||||
plan.append(
|
||||
"po_items",
|
||||
{
|
||||
"use_multi_level_bom": 1,
|
||||
"item_code": "_Test FG Item 3",
|
||||
"bom_no": frappe.db.get_value("Item", "_Test FG Item 3", "default_bom"),
|
||||
"planned_qty": 10,
|
||||
"planned_start_date": now_datetime(),
|
||||
"stock_uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
},
|
||||
)
|
||||
# Sub-assembly item with similar RM item
|
||||
plan.append(
|
||||
"po_items",
|
||||
{
|
||||
"use_multi_level_bom": 1,
|
||||
"item_code": "_Test FG Item 4",
|
||||
"bom_no": frappe.db.get_value("Item", "_Test FG Item 4", "default_bom"),
|
||||
"planned_qty": 10,
|
||||
"planned_start_date": now_datetime(),
|
||||
"stock_uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
},
|
||||
)
|
||||
plan.save()
|
||||
plan.ignore_existing_ordered_qty = 1
|
||||
|
||||
plan.get_sub_assembly_items()
|
||||
|
||||
self.assertEqual(plan.sub_assembly_items[0].qty, 20)
|
||||
self.assertEqual(plan.sub_assembly_items[1].qty, 50)
|
||||
self.assertEqual(plan.sub_assembly_items[0].qty, 20) # Sub Assembly For FG 1
|
||||
self.assertEqual(plan.sub_assembly_items[1].qty, 50) # Sub Assembly For FG 2
|
||||
self.assertEqual(plan.sub_assembly_items[2].qty, 10) # Sub Assembly For FG 4
|
||||
|
||||
from erpnext.manufacturing.doctype.production_plan.production_plan import (
|
||||
get_items_for_material_requests,
|
||||
@@ -1913,8 +1946,11 @@ class TestProductionPlan(IntegrationTestCase):
|
||||
|
||||
mr_items = get_items_for_material_requests(plan.as_dict())
|
||||
|
||||
self.assertEqual(mr_items[0].get("quantity"), 80)
|
||||
self.assertEqual(mr_items[1].get("quantity"), 70)
|
||||
# RM Item 1 (FG1 (100 + 100) + FG2 (50) + FG3 (10) - 90 in stock - 80 sub assembly stock)
|
||||
self.assertEqual(mr_items[0].get("quantity"), 90)
|
||||
|
||||
# RM Item 2 (FG1 (100) + FG2 (50) + FG4 (10) - 80 sub assembly stock)
|
||||
self.assertEqual(mr_items[1].get("quantity"), 80)
|
||||
|
||||
def test_stock_reservation_against_production_plan(self):
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||
|
||||
@@ -767,12 +767,24 @@ erpnext.work_order = {
|
||||
frm.add_custom_button(
|
||||
__("Additional Material Transfer"),
|
||||
function () {
|
||||
erpnext.work_order.make_se(
|
||||
frm,
|
||||
"Material Transfer for Manufacture",
|
||||
qty,
|
||||
1
|
||||
);
|
||||
let purpose = "Material Transfer for Manufacture";
|
||||
erpnext.work_order
|
||||
.show_prompt_for_qty_input(frm, purpose, qty, 1)
|
||||
.then((data) => {
|
||||
return frappe.xcall(
|
||||
"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry",
|
||||
{
|
||||
work_order_id: frm.doc.name,
|
||||
purpose: purpose,
|
||||
qty: data.qty,
|
||||
is_additional_transfer_entry: 1,
|
||||
}
|
||||
);
|
||||
})
|
||||
.then((stock_entry) => {
|
||||
frappe.model.sync(stock_entry);
|
||||
frappe.set_route("Form", stock_entry.doctype, stock_entry.name);
|
||||
});
|
||||
},
|
||||
__("Make")
|
||||
);
|
||||
@@ -940,8 +952,8 @@ erpnext.work_order = {
|
||||
return flt(max, precision("qty"));
|
||||
},
|
||||
|
||||
show_prompt_for_qty_input: function (frm, purpose) {
|
||||
let max = this.get_max_transferable_qty(frm, purpose);
|
||||
show_prompt_for_qty_input: function (frm, purpose, qty, additional_transfer_entry) {
|
||||
let max = !additional_transfer_entry ? this.get_max_transferable_qty(frm, purpose) : qty;
|
||||
|
||||
let fields = [
|
||||
{
|
||||
@@ -951,7 +963,10 @@ erpnext.work_order = {
|
||||
description: __("Max: {0}", [max]),
|
||||
default: max,
|
||||
},
|
||||
{
|
||||
];
|
||||
|
||||
if (!additional_transfer_entry) {
|
||||
fields.push({
|
||||
fieldtype: "Check",
|
||||
label: __("Consider Process Loss"),
|
||||
fieldname: "consider_process_loss",
|
||||
@@ -963,8 +978,8 @@ erpnext.work_order = {
|
||||
frm.qty_prompt.set_value("qty", max);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
if (purpose === "Disassemble") {
|
||||
fields.push({
|
||||
|
||||
@@ -1336,6 +1336,9 @@ class WorkOrder(Document):
|
||||
self.set_available_qty()
|
||||
|
||||
def update_transferred_qty_for_required_items(self):
|
||||
if self.skip_transfer:
|
||||
return
|
||||
|
||||
ste = frappe.qb.DocType("Stock Entry")
|
||||
ste_child = frappe.qb.DocType("Stock Entry Detail")
|
||||
|
||||
|
||||
@@ -31,6 +31,16 @@ class ProductionPlanReport:
|
||||
return self.columns, self.data
|
||||
|
||||
def get_open_orders(self):
|
||||
"""
|
||||
Load open orders into self.orders according to the report filters.
|
||||
|
||||
Builds and executes a query determined by self.filters.based_on and self.filters.order_by:
|
||||
- For "Work Order": selects production fields for active work orders and optionally orders by planned start date or filters by docnames.
|
||||
- For other doctypes: joins with the corresponding "<Doctype> Item" child table to select BOM/item-based production lines; includes special handling for "Sales Order" (filters undelivered/open orders, optional ordering by delivery date or total amount) and "Material Request" (filters manufacture-type requests with outstanding quantity, optional ordering by required date).
|
||||
The query always restricts to submitted documents (docstatus == 1) and applies a company filter when provided.
|
||||
|
||||
After execution, assigns the query result (list of dicts) to self.orders.
|
||||
"""
|
||||
doctype, order_by = self.filters.based_on, self.filters.order_by
|
||||
|
||||
parent = frappe.qb.DocType(doctype)
|
||||
@@ -113,6 +123,13 @@ class ProductionPlanReport:
|
||||
self.orders = query.run(as_dict=True)
|
||||
|
||||
def get_raw_materials(self):
|
||||
"""Retrieve raw materials and source warehouses for production orders.
|
||||
|
||||
This method collects BOM or Work Order items depending on the selected
|
||||
filter and updates `self.raw_materials_dict`, `self.warehouses`,
|
||||
and `self.item_codes` accordingly.
|
||||
"""
|
||||
|
||||
if not self.orders:
|
||||
return
|
||||
self.warehouses = [d.warehouse for d in self.orders]
|
||||
@@ -135,7 +152,7 @@ class ProductionPlanReport:
|
||||
)
|
||||
or []
|
||||
)
|
||||
self.warehouses.extend([d.source_warehouse for d in raw_materials])
|
||||
self.warehouses.extend([d.warehouse for d in raw_materials])
|
||||
|
||||
else:
|
||||
bom_nos = []
|
||||
@@ -426,4 +443,4 @@ class ProductionPlanReport:
|
||||
"width": 140,
|
||||
},
|
||||
]
|
||||
)
|
||||
)
|
||||
@@ -4,6 +4,9 @@ import frappe
|
||||
def execute():
|
||||
# Update the reference_name, reference_doctype fields for Serial No where it is null
|
||||
|
||||
if not frappe.db.has_column("Serial and Batch Bundle", "posting_date"):
|
||||
return
|
||||
|
||||
sabb = frappe.qb.DocType("Serial and Batch Bundle")
|
||||
sabb_entry = frappe.qb.DocType("Serial and Batch Entry")
|
||||
serial_no = frappe.qb.DocType("Serial No").as_("sn")
|
||||
@@ -16,7 +19,7 @@ def execute():
|
||||
.on(sabb.name == sabb_entry.parent)
|
||||
.set(serial_no.reference_name, serial_no.purchase_document_no)
|
||||
.set(serial_no.reference_doctype, sabb.voucher_type)
|
||||
.set(serial_no.posting_date, sabb.posting_datetime)
|
||||
.set(serial_no.posting_date, sabb.posting_date)
|
||||
.where(
|
||||
(sabb.voucher_no == serial_no.purchase_document_no)
|
||||
& (sabb.is_cancelled == 0)
|
||||
|
||||
@@ -174,13 +174,15 @@ erpnext.buying = {
|
||||
shipping_address: this.frm.doc.shipping_address,
|
||||
},
|
||||
callback: (r) => {
|
||||
this.frm.set_value("billing_address", r.message.primary_address || "");
|
||||
if (!this.frm.doc.billing_address)
|
||||
this.frm.set_value("billing_address", r.message.primary_address || "");
|
||||
|
||||
if (!frappe.meta.has_field(this.frm.doc.doctype, "shipping_address")) return;
|
||||
this.frm.set_value(
|
||||
"shipping_address",
|
||||
r.message.shipping_address || this.frm.doc.shipping_address || ""
|
||||
);
|
||||
if (
|
||||
!frappe.meta.has_field(this.frm.doc.doctype, "shipping_address") ||
|
||||
this.frm.doc.shipping_address
|
||||
)
|
||||
return;
|
||||
this.frm.set_value("shipping_address", r.message.shipping_address || "");
|
||||
},
|
||||
});
|
||||
erpnext.utils.set_letter_head(this.frm);
|
||||
|
||||
@@ -1170,7 +1170,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
|
||||
if (
|
||||
frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
|
||||
["Purchase Order", "Purchase Receipt", "Purchase Invoice"].includes(this.frm.doctype)
|
||||
["Purchase Order", "Purchase Receipt", "Purchase Invoice"].includes(this.frm.doctype) &&
|
||||
!this.frm.doc.shipping_address
|
||||
) {
|
||||
let is_drop_ship = me.frm.doc.items.some((item) => item.delivered_by_supplier);
|
||||
|
||||
@@ -1692,6 +1693,17 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
);
|
||||
|
||||
var company_currency = this.get_company_currency();
|
||||
|
||||
if (
|
||||
this._last_company_currency === company_currency &&
|
||||
this._last_price_list_currency === this.frm.doc.price_list_currency
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._last_company_currency = company_currency;
|
||||
this._last_price_list_currency = this.frm.doc.price_list_currency;
|
||||
|
||||
this.change_form_labels(company_currency);
|
||||
this.change_grid_labels(company_currency);
|
||||
this.frm.refresh_fields();
|
||||
|
||||
@@ -20,7 +20,6 @@ frappe.realtime.on("sales_invoice_before_print", (data) => {
|
||||
label: "Website",
|
||||
fieldname: "website",
|
||||
fieldtype: "Data",
|
||||
reqd: data.website ? 0 : 1,
|
||||
hidden: data.website ? 1 : 0,
|
||||
},
|
||||
{
|
||||
@@ -59,12 +58,18 @@ frappe.realtime.on("sales_invoice_before_print", (data) => {
|
||||
hidden: data.address_line ? 1 : 0,
|
||||
},
|
||||
{
|
||||
label: "Address Line",
|
||||
label: "Address Line 1",
|
||||
fieldname: "address_line1",
|
||||
fieldtype: "Data",
|
||||
reqd: data.address_line ? 0 : 1,
|
||||
hidden: data.address_line ? 1 : 0,
|
||||
},
|
||||
{
|
||||
label: "Address Line 2",
|
||||
fieldname: "address_line2",
|
||||
fieldtype: "Data",
|
||||
hidden: data.address_line ? 1 : 0,
|
||||
},
|
||||
{
|
||||
label: "City",
|
||||
fieldname: "city",
|
||||
@@ -99,6 +104,7 @@ frappe.realtime.on("sales_invoice_before_print", (data) => {
|
||||
options: "Address",
|
||||
get_query: function () {
|
||||
return {
|
||||
query: "frappe.contacts.doctype.address.address.address_query",
|
||||
filters: {
|
||||
link_doctype: "Company",
|
||||
link_name: data.company,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<span class="workstation-status-title" style="font-size:10px">{{row.status}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="workstation-image {{row.workstation_off}}" onclick="location.href='{{row.workstation_link}}'">
|
||||
<div class="workstation-image {{row.workstation_off}}" onclick="location.href='{{ frappe.utils.get_form_link('Workstation', row.name)}}'">
|
||||
<div class="workstation-image-container flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
|
||||
{% if(row.status_image) { %}
|
||||
<img class="workstation-image-cls" src="{{row.status_image}}">
|
||||
|
||||
@@ -1011,7 +1011,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
get_query_filters: {
|
||||
company: me.frm.doc.company,
|
||||
docstatus: 1,
|
||||
status: ["!=", "Lost"],
|
||||
status: ["not in", ["Lost", "Ordered"]],
|
||||
},
|
||||
allow_child_item_selection: true,
|
||||
child_fieldname: "items",
|
||||
|
||||
@@ -289,6 +289,7 @@ erpnext.company.setup_queries = function (frm) {
|
||||
["default_provisional_account", { root_type: ["in", ["Liability", "Asset"]] }],
|
||||
["default_advance_received_account", { root_type: "Liability", account_type: "Receivable" }],
|
||||
["default_advance_paid_account", { root_type: "Asset", account_type: "Payable" }],
|
||||
["service_expense_account", { root_type: "Expense" }],
|
||||
],
|
||||
function (i, v) {
|
||||
erpnext.company.set_custom_query(frm, v);
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"transactions_annual_history",
|
||||
"purchase_expense_section",
|
||||
"purchase_expense_account",
|
||||
"service_expense_account",
|
||||
"column_break_ereg",
|
||||
"purchase_expense_contra_account",
|
||||
"stock_tab",
|
||||
@@ -869,6 +870,13 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Purchase Expense Contra Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"description": "For service item",
|
||||
"fieldname": "service_expense_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Service Expense Account",
|
||||
"options": "Account"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-building",
|
||||
@@ -876,7 +884,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2025-10-01 17:34:10.971627",
|
||||
"modified": "2025-10-10 15:12:37.941251",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
||||
@@ -100,6 +100,7 @@ class Company(NestedSet):
|
||||
round_off_for_opening: DF.Link | None
|
||||
sales_monthly_history: DF.SmallText | None
|
||||
series_for_depreciation_entry: DF.Data | None
|
||||
service_expense_account: DF.Link | None
|
||||
stock_adjustment_account: DF.Link | None
|
||||
stock_received_but_not_billed: DF.Link | None
|
||||
submit_err_jv: DF.Check
|
||||
@@ -570,6 +571,21 @@ class Company(NestedSet):
|
||||
|
||||
self.db_set("disposal_account", disposal_acct)
|
||||
|
||||
if not self.service_expense_account:
|
||||
service_expense_acct = frappe.db.get_value(
|
||||
"Account",
|
||||
{
|
||||
"account_name": _("Marketing Expenses"),
|
||||
"company": self.name,
|
||||
"is_group": 0,
|
||||
"root_type": "Expense",
|
||||
},
|
||||
"name",
|
||||
)
|
||||
|
||||
if service_expense_acct:
|
||||
self.db_set("service_expense_account", service_expense_acct)
|
||||
|
||||
def _set_default_account(self, fieldname, account_type):
|
||||
if self.get(fieldname):
|
||||
return
|
||||
|
||||
@@ -44,7 +44,7 @@ class SalesPartner(WebsiteGenerator):
|
||||
load_address_and_contact(self)
|
||||
|
||||
def autoname(self):
|
||||
self.name = self.partner_name
|
||||
pass
|
||||
|
||||
def validate(self):
|
||||
if not self.route:
|
||||
|
||||
@@ -253,6 +253,20 @@ def update_roles():
|
||||
|
||||
def create_default_role_profiles():
|
||||
for role_profile_name, roles in DEFAULT_ROLE_PROFILES.items():
|
||||
if frappe.db.exists("Role Profile", role_profile_name):
|
||||
role_profile = frappe.get_doc("Role Profile", role_profile_name)
|
||||
existing_roles = [row.role for row in role_profile.roles]
|
||||
|
||||
role_profile.roles = [row for row in role_profile.roles if row.role in roles]
|
||||
|
||||
for role in roles:
|
||||
if role not in existing_roles:
|
||||
role_profile.append("roles", {"role": role})
|
||||
|
||||
role_profile.save(ignore_permissions=True)
|
||||
|
||||
continue
|
||||
|
||||
role_profile = frappe.new_doc("Role Profile")
|
||||
role_profile.role_profile = role_profile_name
|
||||
for role in roles:
|
||||
@@ -286,8 +300,8 @@ def create_letter_head():
|
||||
base_path = frappe.get_app_path("erpnext", "accounts", "letterhead")
|
||||
|
||||
letterheads = {
|
||||
"Letterhead with background colour": "letterhead_with_background_colour.html",
|
||||
"Letterhead Plain": "letterhead_plain.html",
|
||||
"Company Letterhead": "company_letterhead.html",
|
||||
"Company Letterhead - Grey": "company_letterhead_grey.html",
|
||||
}
|
||||
|
||||
for name, filename in letterheads.items():
|
||||
|
||||
@@ -81,6 +81,8 @@ def update_page_info(bootinfo):
|
||||
def bootinfo(bootinfo):
|
||||
if bootinfo.get("user") and bootinfo["user"].get("name"):
|
||||
bootinfo["user"]["employee"] = ""
|
||||
frappe.session.data.employee = ""
|
||||
employee = frappe.db.get_value("Employee", {"user_id": bootinfo["user"]["name"]}, "name")
|
||||
if employee:
|
||||
bootinfo["user"]["employee"] = employee
|
||||
frappe.session.data.employee = employee
|
||||
|
||||
@@ -158,7 +158,9 @@ class Batch(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def recalculate_batch_qty(self):
|
||||
batches = get_batch_qty(batch_no=self.name, item_code=self.item)
|
||||
batches = get_batch_qty(
|
||||
batch_no=self.name, item_code=self.item, for_stock_levels=True, consider_negative_batches=True
|
||||
)
|
||||
batch_qty = 0.0
|
||||
if batches:
|
||||
for row in batches:
|
||||
@@ -260,6 +262,7 @@ def get_batch_qty(
|
||||
"warehouse": warehouse,
|
||||
"creation": creation,
|
||||
"batch_no": batch_no,
|
||||
"based_on": frappe.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"),
|
||||
"ignore_voucher_nos": ignore_voucher_nos,
|
||||
"for_stock_levels": for_stock_levels,
|
||||
"consider_negative_batches": consider_negative_batches,
|
||||
|
||||
@@ -2659,6 +2659,127 @@ class TestDeliveryNote(IntegrationTestCase):
|
||||
status = frappe.db.get_value("Serial No", row, "status")
|
||||
self.assertEqual(status, "Active")
|
||||
|
||||
def test_sales_return_for_product_bundle(self):
|
||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
rm_items = []
|
||||
for item_code, properties in {
|
||||
"_Packed Service Item": {"is_stock_item": 0},
|
||||
"_Packed FG Item New 1": {
|
||||
"is_stock_item": 1,
|
||||
"has_serial_no": 1,
|
||||
"serial_no_series": "SN-PACKED-1-.#####",
|
||||
},
|
||||
"_Packed FG Item New 2": {
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "BATCH-PACKED-2-.#####",
|
||||
},
|
||||
"_Packed FG Item New 3": {
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "BATCH-PACKED-3-.#####",
|
||||
"has_serial_no": 1,
|
||||
"serial_no_series": "SN-PACKED-3-.#####",
|
||||
},
|
||||
}.items():
|
||||
if not frappe.db.exists("Item", item_code):
|
||||
make_item(item_code, properties)
|
||||
|
||||
if item_code != "_Packed Service Item":
|
||||
rm_items.append(item_code)
|
||||
|
||||
for rate in [100, 200]:
|
||||
make_stock_entry(item=item_code, target="_Test Warehouse - _TC", qty=5, rate=rate)
|
||||
|
||||
make_product_bundle("_Packed Service Item", rm_items)
|
||||
dn = create_delivery_note(
|
||||
item_code="_Packed Service Item",
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
qty=5,
|
||||
)
|
||||
|
||||
serial_batch_map = {}
|
||||
for row in dn.packed_items:
|
||||
self.assertTrue(row.serial_and_batch_bundle)
|
||||
if row.item_code not in serial_batch_map:
|
||||
serial_batch_map[row.item_code] = frappe._dict(
|
||||
{
|
||||
"serial_nos": [],
|
||||
"batches": defaultdict(int),
|
||||
"serial_no_valuation": defaultdict(float),
|
||||
"batch_no_valuation": defaultdict(float),
|
||||
}
|
||||
)
|
||||
|
||||
doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||
for entry in doc.entries:
|
||||
if entry.serial_no:
|
||||
serial_batch_map[row.item_code].serial_nos.append(entry.serial_no)
|
||||
serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no] = entry.incoming_rate
|
||||
if entry.batch_no:
|
||||
serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty
|
||||
serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no] = entry.incoming_rate
|
||||
|
||||
dn1 = make_sales_return(dn.name)
|
||||
dn1.items[0].qty = -2
|
||||
dn1.submit()
|
||||
dn1.reload()
|
||||
|
||||
for row in dn1.packed_items:
|
||||
doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||
for entry in doc.entries:
|
||||
if entry.serial_no:
|
||||
self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos)
|
||||
self.assertEqual(
|
||||
entry.incoming_rate,
|
||||
serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no],
|
||||
)
|
||||
serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no)
|
||||
serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no)
|
||||
|
||||
elif entry.batch_no:
|
||||
serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty
|
||||
self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches)
|
||||
self.assertEqual(entry.qty, 2.0)
|
||||
self.assertEqual(
|
||||
entry.incoming_rate,
|
||||
serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no],
|
||||
)
|
||||
|
||||
dn2 = make_sales_return(dn.name)
|
||||
dn2.items[0].qty = -3
|
||||
dn2.submit()
|
||||
dn2.reload()
|
||||
|
||||
for row in dn2.packed_items:
|
||||
doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||
for entry in doc.entries:
|
||||
if entry.serial_no:
|
||||
self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos)
|
||||
self.assertEqual(
|
||||
entry.incoming_rate,
|
||||
serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no],
|
||||
)
|
||||
serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no)
|
||||
serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no)
|
||||
|
||||
elif entry.batch_no:
|
||||
serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty
|
||||
self.assertEqual(serial_batch_map[row.item_code].batches[entry.batch_no], 0.0)
|
||||
|
||||
self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches)
|
||||
|
||||
self.assertEqual(entry.qty, 3.0)
|
||||
self.assertEqual(
|
||||
entry.incoming_rate,
|
||||
serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no],
|
||||
)
|
||||
|
||||
|
||||
def create_delivery_note(**args):
|
||||
dn = frappe.new_doc("Delivery Note")
|
||||
|
||||
@@ -200,6 +200,7 @@ erpnext.stock.PurchaseReceiptController = class PurchaseReceiptController extend
|
||||
}
|
||||
|
||||
onload() {
|
||||
super.onload();
|
||||
this.frm.set_query("supplier", function () {
|
||||
return {
|
||||
filters: {
|
||||
|
||||
@@ -1215,7 +1215,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
|
||||
buying_settings = frappe.get_single("Buying Settings")
|
||||
over_billing_allowance = frappe.get_single_value("Accounts Settings", "over_billing_allowance")
|
||||
|
||||
total_amount, total_billed_amount = 0, 0
|
||||
total_amount, total_billed_amount, pi_landed_cost_amount = 0, 0, 0
|
||||
item_wise_returned_qty = get_item_wise_returned_qty(pr_doc)
|
||||
|
||||
if adjust_incoming_rate:
|
||||
@@ -1255,6 +1255,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
|
||||
) * item.qty
|
||||
|
||||
adjusted_amt = flt(adjusted_amt * flt(pr_doc.conversion_rate), item.precision("amount"))
|
||||
pi_landed_cost_amount += adjusted_amt
|
||||
item.db_set("amount_difference_with_purchase_invoice", adjusted_amt, update_modified=False)
|
||||
elif amount and item.billed_amt > amount:
|
||||
per_over_billed = (flt(item.billed_amt / amount, 2) * 100) - 100
|
||||
@@ -1265,6 +1266,9 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
|
||||
)
|
||||
)
|
||||
|
||||
if pi_landed_cost_amount < 0:
|
||||
total_billed_amount += abs(pi_landed_cost_amount)
|
||||
|
||||
percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
|
||||
pr_doc.db_set("per_billed", percent_billed)
|
||||
|
||||
|
||||
@@ -638,6 +638,17 @@ class SerialandBatchBundle(Document):
|
||||
if not rate and self.voucher_detail_no and self.voucher_no:
|
||||
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
||||
|
||||
is_packed_item = False
|
||||
if rate is None and child_table in ["Delivery Note Item", "Sales Invoice Item"]:
|
||||
rate = frappe.db.get_value(
|
||||
"Packed Item",
|
||||
self.voucher_detail_no,
|
||||
"incoming_rate",
|
||||
)
|
||||
|
||||
if rate is not None:
|
||||
is_packed_item = True
|
||||
|
||||
stock_queue = []
|
||||
batches = []
|
||||
if prev_sle and prev_sle.stock_queue:
|
||||
@@ -659,6 +670,9 @@ class SerialandBatchBundle(Document):
|
||||
elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference:
|
||||
continue
|
||||
|
||||
if is_packed_item and d.incoming_rate:
|
||||
rate = d.incoming_rate
|
||||
|
||||
d.incoming_rate = flt(rate)
|
||||
if d.qty:
|
||||
d.stock_value_difference = flt(d.qty) * d.incoming_rate
|
||||
@@ -2337,15 +2351,15 @@ def get_reserved_batches_for_sre(kwargs) -> dict:
|
||||
|
||||
if kwargs.batch_no:
|
||||
if isinstance(kwargs.batch_no, list):
|
||||
query = query.where(sb_entry.batch_no.notin(kwargs.batch_no))
|
||||
query = query.where(sb_entry.batch_no.isin(kwargs.batch_no))
|
||||
else:
|
||||
query = query.where(sb_entry.batch_no != kwargs.batch_no)
|
||||
query = query.where(sb_entry.batch_no == kwargs.batch_no)
|
||||
|
||||
if kwargs.warehouse:
|
||||
if isinstance(kwargs.warehouse, list):
|
||||
query = query.where(sre.warehouse.notin(kwargs.warehouse))
|
||||
query = query.where(sre.warehouse.isin(kwargs.warehouse))
|
||||
else:
|
||||
query = query.where(sre.warehouse != kwargs.warehouse)
|
||||
query = query.where(sre.warehouse == kwargs.warehouse)
|
||||
|
||||
if kwargs.ignore_voucher_nos:
|
||||
query = query.where(sre.name.notin(kwargs.ignore_voucher_nos))
|
||||
|
||||
@@ -706,7 +706,6 @@
|
||||
"depends_on": "eval:doc.purpose == \"Material Transfer for Manufacture\"",
|
||||
"fieldname": "is_additional_transfer_entry",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Is Additional Transfer Entry",
|
||||
"read_only": 1
|
||||
}
|
||||
@@ -716,7 +715,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-09-29 15:56:21.344296",
|
||||
"modified": "2025-10-13 15:09:23.905118",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
|
||||
@@ -2106,10 +2106,13 @@ class StockEntry(StockController):
|
||||
if not frappe.get_cached_value("Work Order", self.work_order, "reserve_stock"):
|
||||
return
|
||||
|
||||
skip_transfer = frappe.get_cached_value("Work Order", self.work_order, "skip_transfer")
|
||||
|
||||
if (
|
||||
self.purpose not in ["Material Transfer for Manufacture"]
|
||||
and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")
|
||||
!= "BOM"
|
||||
and not skip_transfer
|
||||
):
|
||||
return
|
||||
|
||||
@@ -2172,6 +2175,10 @@ class StockEntry(StockController):
|
||||
self.append("items", new_row)
|
||||
|
||||
sorted_items = sorted(self.items, key=lambda x: x.item_code)
|
||||
if self.purpose == "Manufacture":
|
||||
# ensure finished item at last
|
||||
sorted_items = sorted(sorted_items, key=lambda x: (x.t_warehouse))
|
||||
|
||||
idx = 0
|
||||
for row in sorted_items:
|
||||
idx += 1
|
||||
|
||||
@@ -613,10 +613,10 @@ class StockReservationEntry(Document):
|
||||
data = row_wise_serial_batch[row]
|
||||
|
||||
if entry.serial_no in data.serial_nos:
|
||||
entry.delivered_qty = 1
|
||||
entry.delivered_qty = flt(1)
|
||||
|
||||
elif entry.batch_no in data.batch_nos:
|
||||
entry.delivered_qty = data.batch_nos[entry.batch_no]
|
||||
entry.delivered_qty = flt(data.batch_nos[entry.batch_no])
|
||||
|
||||
entry.db_update()
|
||||
|
||||
|
||||
@@ -502,6 +502,9 @@ def get_basic_details(ctx: ItemDetailsCtx, item, overwrite_warehouse=True) -> It
|
||||
}
|
||||
)
|
||||
|
||||
if not item.is_stock_item and not out.expense_account:
|
||||
out.expense_account = frappe.get_cached_value("Company", ctx.company, "service_expense_account")
|
||||
|
||||
default_supplier = get_default_supplier(ctx, item_defaults, item_group_defaults, brand_defaults)
|
||||
if default_supplier:
|
||||
out.supplier = default_supplier
|
||||
|
||||
@@ -1293,6 +1293,12 @@ class SerialBatchCreation:
|
||||
if self.get("voucher_type"):
|
||||
voucher_type = self.get("voucher_type")
|
||||
|
||||
posting_date = frappe.db.get_value(
|
||||
voucher_type,
|
||||
voucher_no,
|
||||
"posting_date",
|
||||
)
|
||||
|
||||
for _i in range(abs(cint(self.actual_qty))):
|
||||
serial_no = make_autoname(self.serial_no_series, "Serial No")
|
||||
sr_nos.append(serial_no)
|
||||
@@ -1312,6 +1318,7 @@ class SerialBatchCreation:
|
||||
"Active",
|
||||
voucher_type,
|
||||
voucher_no,
|
||||
posting_date,
|
||||
self.batch_no,
|
||||
)
|
||||
)
|
||||
@@ -1332,6 +1339,7 @@ class SerialBatchCreation:
|
||||
"status",
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
"posting_date",
|
||||
"batch_no",
|
||||
]
|
||||
|
||||
|
||||
@@ -892,9 +892,8 @@ class update_entries_after:
|
||||
sle.stock_value = self.wh_data.stock_value
|
||||
sle.stock_queue = json.dumps(self.wh_data.stock_queue)
|
||||
|
||||
if not sle.is_adjustment_entry:
|
||||
sle.stock_value_difference = stock_value_difference
|
||||
elif sle.is_adjustment_entry and not self.args.get("sle_id"):
|
||||
sle.stock_value_difference = stock_value_difference
|
||||
if sle.is_adjustment_entry and flt(sle.qty_after_transaction, self.flt_precision) == 0:
|
||||
sle.stock_value_difference = (
|
||||
get_stock_value_difference(
|
||||
sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time, sle.voucher_no
|
||||
@@ -1074,16 +1073,15 @@ class update_entries_after:
|
||||
|
||||
for d in sabb_data:
|
||||
incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj)
|
||||
|
||||
if flt(incoming_rate, self.currency_precision) == flt(
|
||||
d.valuation_rate, self.currency_precision
|
||||
) and not getattr(d, "stock_queue", None):
|
||||
continue
|
||||
|
||||
amount = incoming_rate * flt(d.qty)
|
||||
tot_amt += flt(amount)
|
||||
total_qty += flt(d.qty)
|
||||
|
||||
if flt(incoming_rate, self.currency_precision) == flt(
|
||||
d.incoming_rate, self.currency_precision
|
||||
) and not getattr(d, "stock_queue", None):
|
||||
continue
|
||||
|
||||
values_to_update = {
|
||||
"incoming_rate": incoming_rate,
|
||||
"stock_value_difference": amount,
|
||||
|
||||
@@ -336,6 +336,10 @@ frappe.ui.form.on("Subcontracting Receipt", {
|
||||
|
||||
reset_raw_materials_table: (frm) => {
|
||||
frm.clear_table("supplied_items");
|
||||
frm.doc.__unsaved = true;
|
||||
if (!frm.doc.set_posting_time) {
|
||||
frm.set_value("posting_time", frappe.datetime.now_time());
|
||||
}
|
||||
|
||||
frm.call({
|
||||
method: "reset_raw_materials",
|
||||
|
||||
@@ -649,6 +649,7 @@
|
||||
"label": "Raw Materials Actions"
|
||||
},
|
||||
{
|
||||
"description": "Click this button if you encounter a negative stock error for a serial or batch item. The system will fetch the available serials or batches automatically.",
|
||||
"fieldname": "reset_raw_materials_table",
|
||||
"fieldtype": "Button",
|
||||
"label": "Reset Raw Materials Table"
|
||||
@@ -678,7 +679,7 @@
|
||||
"in_create": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-12-06 15:24:38.384232",
|
||||
"modified": "2025-10-08 21:43:27.065640",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Receipt",
|
||||
@@ -739,6 +740,7 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "status, posting_date, supplier",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "creation",
|
||||
@@ -747,4 +749,4 @@
|
||||
"timeline_field": "supplier",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user