mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-24 21:25:04 +00:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a53e823f0 | ||
|
|
c808607975 | ||
|
|
8cb0f690d5 | ||
|
|
4789ecacea | ||
|
|
d00257ffd7 | ||
|
|
37b1a0e778 | ||
|
|
6952f0f082 | ||
|
|
c84384aa1b | ||
|
|
428f7a61ef | ||
|
|
8f4ded6ad1 | ||
|
|
9607d69ada | ||
|
|
aaba335273 | ||
|
|
13d5eec194 | ||
|
|
411bba1b98 | ||
|
|
49f2d868c7 | ||
|
|
335b6c84db | ||
|
|
00dff0a219 | ||
|
|
b55b428114 | ||
|
|
dd6498d347 | ||
|
|
5669a89afe | ||
|
|
6e871c51ef | ||
|
|
b5b51879ee | ||
|
|
4fc45b035a | ||
|
|
24103554f5 | ||
|
|
d459421175 | ||
|
|
bc907b22d4 | ||
|
|
7e26449b9f | ||
|
|
49d0ab5867 | ||
|
|
ddec91202a | ||
|
|
fe681acaad | ||
|
|
52aff1f703 | ||
|
|
1cfc6cfe5d | ||
|
|
7805c3acf6 | ||
|
|
8b4e69235a | ||
|
|
35a62e2e8d | ||
|
|
f6b35324ef | ||
|
|
66ad823417 | ||
|
|
f674635ecf | ||
|
|
0000c38563 | ||
|
|
23f12f6463 | ||
|
|
6f6db432b5 | ||
|
|
ba7a822682 | ||
|
|
32ae68eb5c | ||
|
|
9eb02637d8 | ||
|
|
fd8543ebd3 | ||
|
|
22fb65621c | ||
|
|
55dfd8e995 | ||
|
|
fa0eef96b9 | ||
|
|
ee42b0a16b | ||
|
|
40e475836e | ||
|
|
1da808a125 | ||
|
|
c86cd99395 | ||
|
|
a3fd4db450 | ||
|
|
76919c4af2 | ||
|
|
b0708d29a8 | ||
|
|
caa8417306 | ||
|
|
ff6b38c9e7 | ||
|
|
4285bbcdc0 | ||
|
|
b6dc47ec8a | ||
|
|
d0c6f286cf | ||
|
|
a6bef64c8e | ||
|
|
5092ea175e | ||
|
|
5833c4dae2 | ||
|
|
48eb6a6573 | ||
|
|
7c1288f726 | ||
|
|
c89715d6da | ||
|
|
d4f8b057e1 | ||
|
|
86b152fe5c | ||
|
|
11b16569e5 | ||
|
|
cd99630457 | ||
|
|
efab1e7361 | ||
|
|
63a6e7e35c | ||
|
|
d7f09f8795 | ||
|
|
437a294621 | ||
|
|
9a15ed8083 | ||
|
|
3a49d4f9b3 | ||
|
|
8b2c6ed61a | ||
|
|
4f98e958a1 | ||
|
|
3a71fa9d96 | ||
|
|
6f40d0cdf6 | ||
|
|
5bd6c27b05 | ||
|
|
9876019c69 | ||
|
|
f4fb878282 | ||
|
|
783bb93913 | ||
|
|
cfbd9af100 | ||
|
|
966c296872 | ||
|
|
0ff871e38e | ||
|
|
9df10dbc40 | ||
|
|
bdaae81171 | ||
|
|
066cf0e3bc | ||
|
|
829298066f | ||
|
|
006da22d3f | ||
|
|
3b9ac9f46a | ||
|
|
8f977f40f0 | ||
|
|
3bc7c88133 | ||
|
|
84b6f68108 | ||
|
|
e5b38607ce | ||
|
|
b3c9d0d910 | ||
|
|
c417365e03 | ||
|
|
b2a4175d43 | ||
|
|
a801bba83e | ||
|
|
3ccb511e25 | ||
|
|
8c51f2e5a1 | ||
|
|
5dbca09899 | ||
|
|
9ec7bb9be3 | ||
|
|
0281afcead | ||
|
|
348e4616cb | ||
|
|
a165b37fd7 | ||
|
|
cdc86bd76c | ||
|
|
e9df06406f | ||
|
|
c574494ddd | ||
|
|
46966f4b7c | ||
|
|
ded3b62c5a | ||
|
|
a5e1c4798f | ||
|
|
bde9e89582 | ||
|
|
e4f28e8a5b | ||
|
|
128ea0d7fc | ||
|
|
9566f4101d | ||
|
|
4854c2e7f7 | ||
|
|
a93c4b6c65 | ||
|
|
287c67f7a2 | ||
|
|
120de249dd | ||
|
|
cefa78b864 | ||
|
|
73366de12f | ||
|
|
1d3917b335 | ||
|
|
3bc899f354 | ||
|
|
6b113c6abc | ||
|
|
82e2ff8731 | ||
|
|
2993eb5ce9 | ||
|
|
12e27e96a8 | ||
|
|
30c9b15cdd | ||
|
|
1d6bc68d87 | ||
|
|
ba69be1ced | ||
|
|
4af57a7318 | ||
|
|
e05bb103f3 | ||
|
|
6f2fb89c49 | ||
|
|
beaf13e00e | ||
|
|
9c3ec41803 | ||
|
|
06c85fa252 | ||
|
|
0248fee533 | ||
|
|
5aa02b8571 | ||
|
|
53f7764c67 | ||
|
|
cf3ec935a7 | ||
|
|
2732276498 | ||
|
|
77eb11a6e3 | ||
|
|
5cfe4195ba | ||
|
|
0feb393fff | ||
|
|
4b20f2a083 | ||
|
|
b992366246 | ||
|
|
93acde7748 | ||
|
|
3a00052b49 | ||
|
|
b04c190e33 |
38
.github/workflows/release_notes.yml
vendored
Normal file
38
.github/workflows/release_notes.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# This action:
|
||||
#
|
||||
# 1. Generates release notes using github API.
|
||||
# 2. Strips unnecessary info like chore/style etc from notes.
|
||||
# 3. Updates release info.
|
||||
|
||||
# This action needs to be maintained on all branches that do releases.
|
||||
|
||||
name: 'Release Notes'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag_name:
|
||||
description: 'Tag of release like v13.0.0'
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
regen-notes:
|
||||
name: 'Regenerate release notes'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Update notes
|
||||
run: |
|
||||
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
|
||||
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
|
||||
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}
|
||||
@@ -4,7 +4,7 @@ import frappe
|
||||
|
||||
from erpnext.hooks import regional_overrides
|
||||
|
||||
__version__ = "13.52.2"
|
||||
__version__ = "13.54.10"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
frappe.ui.form.on('Accounts Settings', {
|
||||
refresh: function(frm) {
|
||||
|
||||
},
|
||||
validate_access_key(frm) {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "validate_access_key"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
"currency_exchange_section",
|
||||
"allow_stale",
|
||||
"stale_days",
|
||||
"service_provider",
|
||||
"column_break_eiyok",
|
||||
"access_key",
|
||||
"validate_access_key",
|
||||
"report_settings_sb",
|
||||
"use_custom_cash_flow"
|
||||
],
|
||||
@@ -282,32 +286,56 @@
|
||||
"label": "Enable Common Party Accounting"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency",
|
||||
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow multi-currency invoices against single party account"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Split Early Payment Discount Loss into Income and Tax Loss",
|
||||
"fieldname": "book_tax_discount_loss",
|
||||
"fieldtype": "Check",
|
||||
"label": "Book Tax Loss on Early Payment Discount"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_taxes_as_table_in_print",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Taxes as Table in Print"
|
||||
}
|
||||
"default": "0",
|
||||
"description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency",
|
||||
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow multi-currency invoices against single party account"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Split Early Payment Discount Loss into Income and Tax Loss",
|
||||
"fieldname": "book_tax_discount_loss",
|
||||
"fieldtype": "Check",
|
||||
"label": "Book Tax Loss on Early Payment Discount"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_taxes_as_table_in_print",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Taxes as Table in Print"
|
||||
},
|
||||
{
|
||||
"default": "frankfurter.app",
|
||||
"fieldname": "service_provider",
|
||||
"fieldtype": "Select",
|
||||
"label": "Service Provider",
|
||||
"options": "frankfurter.app\nexchangerate.host"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.service_provider == \"exchangerate.host\"",
|
||||
"description": "Access Key is mandatory for exchangerate.host",
|
||||
"fieldname": "access_key",
|
||||
"fieldtype": "Data",
|
||||
"label": "Access Key"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.service_provider == \"exchangerate.host\"",
|
||||
"fieldname": "validate_access_key",
|
||||
"fieldtype": "Button",
|
||||
"label": "Validate Access Key"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_eiyok",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-06-13 18:47:46.430291",
|
||||
"modified": "2023-10-07 14:20:01.779208",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
||||
@@ -8,12 +8,43 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
from frappe.utils import cint, nowdate
|
||||
|
||||
from erpnext.stock.utils import check_pending_reposting
|
||||
|
||||
|
||||
class AccountsSettings(Document):
|
||||
@frappe.whitelist()
|
||||
def validate_access_key(self):
|
||||
if self.service_provider == "exchangerate.host":
|
||||
if not self.access_key:
|
||||
frappe.throw(_("Access Key is required for exchangerate.host"))
|
||||
else:
|
||||
import requests
|
||||
|
||||
# Validate access key
|
||||
api_url = "https://api.exchangerate.host/convert"
|
||||
response = requests.get(
|
||||
api_url,
|
||||
params={
|
||||
"access_key": self.access_key,
|
||||
"transaction_date": nowdate(),
|
||||
"amount": 1,
|
||||
"from": "USD",
|
||||
"to": "INR",
|
||||
},
|
||||
)
|
||||
# exchangerate.host return 200 for all requests. Can't rely on it to raise exception
|
||||
if not response.json()["success"]:
|
||||
frappe.throw(
|
||||
title=_("Service Provider Error"),
|
||||
msg=_("Currency exchange rate serivce provider: {0} returned Error. {1}").format(
|
||||
frappe.bold(self.service_provider), response.json()
|
||||
),
|
||||
exc=frappe.ValidationError,
|
||||
)
|
||||
frappe.msgprint(msg=_("Success"), title=_("Access Key Validation"))
|
||||
|
||||
def on_update(self):
|
||||
frappe.clear_cache()
|
||||
|
||||
|
||||
@@ -400,6 +400,15 @@ class JournalEntry(AccountsController):
|
||||
d.idx, d.account
|
||||
)
|
||||
)
|
||||
elif (
|
||||
d.party_type
|
||||
and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row {0}: Account {1} and Party Type {2} have different account types").format(
|
||||
d.idx, d.account, d.party_type
|
||||
)
|
||||
)
|
||||
|
||||
def check_credit_limit(self):
|
||||
customers = list(
|
||||
|
||||
@@ -7,7 +7,16 @@ from functools import reduce
|
||||
|
||||
import frappe
|
||||
from frappe import ValidationError, _, scrub, throw
|
||||
from frappe.utils import cint, comma_or, flt, get_link_to_form, getdate, nowdate
|
||||
from frappe.utils import (
|
||||
cint,
|
||||
comma_and,
|
||||
comma_or,
|
||||
flt,
|
||||
fmt_money,
|
||||
get_link_to_form,
|
||||
getdate,
|
||||
nowdate,
|
||||
)
|
||||
from six import iteritems, string_types
|
||||
|
||||
import erpnext
|
||||
@@ -191,17 +200,17 @@ class PaymentEntry(AccountsController):
|
||||
# The reference has already been fully paid
|
||||
if not latest:
|
||||
frappe.throw(
|
||||
_("{0} {1} has already been fully paid.").format(d.reference_doctype, d.reference_name)
|
||||
_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name)
|
||||
)
|
||||
# The reference has already been partly paid
|
||||
elif (
|
||||
latest.outstanding_amount < latest.invoice_amount
|
||||
and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount
|
||||
):
|
||||
elif latest.outstanding_amount < latest.invoice_amount and flt(
|
||||
d.outstanding_amount, d.precision("outstanding_amount")
|
||||
) != flt(latest.outstanding_amount, d.precision("outstanding_amount")):
|
||||
|
||||
frappe.throw(
|
||||
_(
|
||||
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
|
||||
).format(d.reference_doctype, d.reference_name)
|
||||
).format(_(d.reference_doctype), d.reference_name)
|
||||
)
|
||||
|
||||
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
||||
@@ -319,7 +328,7 @@ class PaymentEntry(AccountsController):
|
||||
def validate_party_details(self):
|
||||
if self.party:
|
||||
if not frappe.db.exists(self.party_type, self.party):
|
||||
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
|
||||
frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party))
|
||||
|
||||
def set_exchange_rate(self, ref_doc=None):
|
||||
self.set_source_exchange_rate(ref_doc)
|
||||
@@ -376,7 +385,9 @@ class PaymentEntry(AccountsController):
|
||||
continue
|
||||
if d.reference_doctype not in valid_reference_doctypes:
|
||||
frappe.throw(
|
||||
_("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes))
|
||||
_("Reference Doctype must be one of {0}").format(
|
||||
comma_or((_(d) for d in valid_reference_doctypes))
|
||||
)
|
||||
)
|
||||
|
||||
elif d.reference_name:
|
||||
@@ -389,7 +400,7 @@ class PaymentEntry(AccountsController):
|
||||
if self.party != ref_doc.get(scrub(self.party_type)):
|
||||
frappe.throw(
|
||||
_("{0} {1} is not associated with {2} {3}").format(
|
||||
d.reference_doctype, d.reference_name, self.party_type, self.party
|
||||
_(d.reference_doctype), d.reference_name, _(self.party_type), self.party
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -410,18 +421,18 @@ class PaymentEntry(AccountsController):
|
||||
if ref_party_account != self.party_account:
|
||||
frappe.throw(
|
||||
_("{0} {1} is associated with {2}, but Party Account is {3}").format(
|
||||
d.reference_doctype, d.reference_name, ref_party_account, self.party_account
|
||||
_(d.reference_doctype), d.reference_name, ref_party_account, self.party_account
|
||||
)
|
||||
)
|
||||
|
||||
if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"):
|
||||
frappe.throw(
|
||||
_("{0} {1} is on hold").format(d.reference_doctype, d.reference_name),
|
||||
title=_("Invalid Invoice"),
|
||||
_("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name),
|
||||
title=_("Invalid Purchase Invoice"),
|
||||
)
|
||||
|
||||
if ref_doc.docstatus != 1:
|
||||
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
|
||||
frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name))
|
||||
|
||||
def validate_paid_invoices(self):
|
||||
no_oustanding_refs = {}
|
||||
@@ -437,14 +448,13 @@ class PaymentEntry(AccountsController):
|
||||
if outstanding_amount <= 0 and not is_return:
|
||||
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
|
||||
|
||||
for k, v in no_oustanding_refs.items():
|
||||
for reference_doctype, references in no_oustanding_refs.items():
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"{} - {} now has {} as it had no outstanding amount left before submitting the Payment Entry."
|
||||
"References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount."
|
||||
).format(
|
||||
_(k),
|
||||
frappe.bold(", ".join(d.reference_name for d in v)),
|
||||
frappe.bold(_("negative outstanding amount")),
|
||||
frappe.bold(comma_and((d.reference_name for d in references))),
|
||||
_(reference_doctype),
|
||||
)
|
||||
+ "<br><br>"
|
||||
+ _("If this is undesirable please cancel the corresponding Payment Entry."),
|
||||
@@ -479,7 +489,7 @@ class PaymentEntry(AccountsController):
|
||||
if not valid:
|
||||
frappe.throw(
|
||||
_("Against Journal Entry {0} does not have any unmatched {1} entry").format(
|
||||
d.reference_name, dr_or_cr
|
||||
d.reference_name, _(dr_or_cr)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -546,7 +556,7 @@ class PaymentEntry(AccountsController):
|
||||
if allocated_amount > outstanding:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Cannot allocate more than {1} against payment term {2}").format(
|
||||
idx, outstanding, key[0]
|
||||
idx, fmt_money(outstanding), key[0]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -606,7 +616,9 @@ class PaymentEntry(AccountsController):
|
||||
if not self.apply_tax_withholding_amount:
|
||||
return
|
||||
|
||||
net_total = self.paid_amount
|
||||
order_amount = self.get_order_net_total()
|
||||
|
||||
net_total = flt(order_amount) + flt(self.unallocated_amount)
|
||||
|
||||
# Adding args as purchase invoice to get TDS amount
|
||||
args = frappe._dict(
|
||||
@@ -651,6 +663,20 @@ class PaymentEntry(AccountsController):
|
||||
for d in to_remove:
|
||||
self.remove(d)
|
||||
|
||||
def get_order_net_total(self):
|
||||
if self.party_type == "Supplier":
|
||||
doctype = "Purchase Order"
|
||||
else:
|
||||
doctype = "Sales Order"
|
||||
|
||||
docnames = [d.reference_name for d in self.references if d.reference_doctype == doctype]
|
||||
|
||||
tax_withholding_net_total = frappe.db.get_value(
|
||||
doctype, {"name": ["in", docnames]}, ["sum(base_net_total)"]
|
||||
)
|
||||
|
||||
return tax_withholding_net_total
|
||||
|
||||
def apply_taxes(self):
|
||||
self.initialize_taxes()
|
||||
self.determine_exclusive_rate()
|
||||
@@ -823,7 +849,7 @@ class PaymentEntry(AccountsController):
|
||||
_("Cannot {0} {1} {2} without any negative outstanding invoice").format(
|
||||
_(self.payment_type),
|
||||
(_("to") if self.party_type == "Customer" else _("from")),
|
||||
self.party_type,
|
||||
_(self.party_type),
|
||||
),
|
||||
InvalidPaymentEntry,
|
||||
)
|
||||
@@ -831,7 +857,7 @@ class PaymentEntry(AccountsController):
|
||||
elif paid_amount - additional_charges > total_negative_outstanding:
|
||||
frappe.throw(
|
||||
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
|
||||
total_negative_outstanding
|
||||
fmt_money(total_negative_outstanding)
|
||||
),
|
||||
InvalidPaymentEntry,
|
||||
)
|
||||
@@ -1327,6 +1353,9 @@ def get_outstanding_reference_documents(args):
|
||||
if args.get("party_type") == "Member":
|
||||
return
|
||||
|
||||
if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"):
|
||||
args["get_outstanding_invoices"] = True
|
||||
|
||||
# confirm that Supplier is not blocked
|
||||
if args.get("party_type") == "Supplier":
|
||||
supplier_status = get_supplier_block_status(args["party"])
|
||||
@@ -1433,7 +1462,7 @@ def get_outstanding_reference_documents(args):
|
||||
_(
|
||||
"No outstanding {0} found for the {1} {2} which qualify the filters you have specified."
|
||||
).format(
|
||||
ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
|
||||
_(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1508,60 +1537,59 @@ def get_orders_to_be_billed(
|
||||
cost_center=None,
|
||||
filters=None,
|
||||
):
|
||||
voucher_type = None
|
||||
if party_type == "Customer":
|
||||
voucher_type = "Sales Order"
|
||||
elif party_type == "Supplier":
|
||||
voucher_type = "Purchase Order"
|
||||
elif party_type == "Employee":
|
||||
voucher_type = None
|
||||
|
||||
if not voucher_type:
|
||||
return []
|
||||
|
||||
# Add cost center condition
|
||||
if voucher_type:
|
||||
doc = frappe.get_doc({"doctype": voucher_type})
|
||||
condition = ""
|
||||
if doc and hasattr(doc, "cost_center") and doc.cost_center:
|
||||
condition = " and cost_center='%s'" % cost_center
|
||||
doc = frappe.get_doc({"doctype": voucher_type})
|
||||
condition = ""
|
||||
if doc and hasattr(doc, "cost_center") and doc.cost_center:
|
||||
condition = " and cost_center='%s'" % cost_center
|
||||
|
||||
orders = []
|
||||
if voucher_type:
|
||||
if party_account_currency == company_currency:
|
||||
grand_total_field = "base_grand_total"
|
||||
rounded_total_field = "base_rounded_total"
|
||||
else:
|
||||
grand_total_field = "grand_total"
|
||||
rounded_total_field = "rounded_total"
|
||||
if party_account_currency == company_currency:
|
||||
grand_total_field = "base_grand_total"
|
||||
rounded_total_field = "base_rounded_total"
|
||||
else:
|
||||
grand_total_field = "grand_total"
|
||||
rounded_total_field = "rounded_total"
|
||||
|
||||
orders = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name as voucher_no,
|
||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
||||
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
||||
transaction_date as posting_date
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
where
|
||||
{party_type} = %s
|
||||
and docstatus = 1
|
||||
and company = %s
|
||||
and ifnull(status, "") != "Closed"
|
||||
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
||||
and abs(100 - per_billed) > 0.01
|
||||
{condition}
|
||||
order by
|
||||
transaction_date, name
|
||||
""".format(
|
||||
**{
|
||||
"rounded_total_field": rounded_total_field,
|
||||
"grand_total_field": grand_total_field,
|
||||
"voucher_type": voucher_type,
|
||||
"party_type": scrub(party_type),
|
||||
"condition": condition,
|
||||
}
|
||||
),
|
||||
(party, company),
|
||||
as_dict=True,
|
||||
)
|
||||
orders = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name as voucher_no,
|
||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
||||
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
||||
transaction_date as posting_date
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
where
|
||||
{party_type} = %s
|
||||
and docstatus = 1
|
||||
and company = %s
|
||||
and ifnull(status, "") != "Closed"
|
||||
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
||||
and abs(100 - per_billed) > 0.01
|
||||
{condition}
|
||||
order by
|
||||
transaction_date, name
|
||||
""".format(
|
||||
**{
|
||||
"rounded_total_field": rounded_total_field,
|
||||
"grand_total_field": grand_total_field,
|
||||
"voucher_type": voucher_type,
|
||||
"party_type": scrub(party_type),
|
||||
"condition": condition,
|
||||
}
|
||||
),
|
||||
(party, company),
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
order_list = []
|
||||
for d in orders:
|
||||
@@ -1594,6 +1622,8 @@ def get_negative_outstanding_invoices(
|
||||
cost_center=None,
|
||||
condition=None,
|
||||
):
|
||||
if party_type not in ["Customer", "Supplier"]:
|
||||
return []
|
||||
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
||||
supplier_condition = ""
|
||||
if voucher_type == "Purchase Invoice":
|
||||
@@ -1642,7 +1672,7 @@ def get_negative_outstanding_invoices(
|
||||
def get_party_details(company, party_type, party, date, cost_center=None):
|
||||
bank_account = ""
|
||||
if not frappe.db.exists(party_type, party):
|
||||
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
|
||||
frappe.throw(_("{0} {1} does not exist").format(_(party_type), party))
|
||||
|
||||
party_account = get_party_account(party_type, party, company)
|
||||
|
||||
@@ -1927,7 +1957,7 @@ def get_payment_entry(
|
||||
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
|
||||
100.0 + over_billing_allowance
|
||||
):
|
||||
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
||||
frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt)))
|
||||
|
||||
party_type = set_party_type(dt)
|
||||
party_account = set_party_account(dt, dn, doc, party_type)
|
||||
|
||||
@@ -345,7 +345,8 @@
|
||||
"no_copy": 1,
|
||||
"options": "POS Invoice",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -1572,7 +1573,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-09-27 13:00:24.166684",
|
||||
"modified": "2022-09-30 03:49:50.455199",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import collections
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
@@ -44,6 +44,7 @@ class POSInvoice(SalesInvoice):
|
||||
self.validate_debit_to_acc()
|
||||
self.validate_write_off_account()
|
||||
self.validate_change_amount()
|
||||
self.validate_duplicate_serial_and_batch_no()
|
||||
self.validate_change_account()
|
||||
self.validate_item_cost_centers()
|
||||
self.validate_warehouse()
|
||||
@@ -154,6 +155,27 @@ class POSInvoice(SalesInvoice):
|
||||
title=_("Item Unavailable"),
|
||||
)
|
||||
|
||||
def validate_duplicate_serial_and_batch_no(self):
|
||||
serial_nos = []
|
||||
batch_nos = []
|
||||
|
||||
for row in self.get("items"):
|
||||
if row.serial_no:
|
||||
serial_nos = row.serial_no.split("\n")
|
||||
|
||||
if row.batch_no and not row.serial_no:
|
||||
batch_nos.append(row.batch_no)
|
||||
|
||||
if serial_nos:
|
||||
for key, value in collections.Counter(serial_nos).items():
|
||||
if value > 1:
|
||||
frappe.throw(_("Duplicate Serial No {0} found").format("key"))
|
||||
|
||||
if batch_nos:
|
||||
for key, value in collections.Counter(batch_nos).items():
|
||||
if value > 1:
|
||||
frappe.throw(_("Duplicate Batch No {0} found").format("key"))
|
||||
|
||||
def validate_pos_reserved_batch_qty(self, item):
|
||||
filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no}
|
||||
|
||||
@@ -752,7 +774,3 @@ def add_return_modes(doc, pos_profile):
|
||||
]:
|
||||
payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
|
||||
append_payment(payment_mode[0])
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("POS Invoice", ["return_against"])
|
||||
|
||||
@@ -261,9 +261,7 @@ class PurchaseInvoice(BuyingController):
|
||||
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
|
||||
stock_items = self.get_stock_items()
|
||||
|
||||
asset_items = [d.is_fixed_asset for d in self.items if d.is_fixed_asset]
|
||||
if len(asset_items) > 0:
|
||||
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
|
||||
asset_received_but_not_billed = None
|
||||
|
||||
if self.update_stock:
|
||||
self.validate_item_code()
|
||||
@@ -357,6 +355,8 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
item.expense_account = asset_category_account
|
||||
elif item.is_fixed_asset and item.pr_detail:
|
||||
if not asset_received_but_not_billed:
|
||||
asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
|
||||
item.expense_account = asset_received_but_not_billed
|
||||
elif not item.expense_account and for_validate:
|
||||
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
||||
@@ -924,8 +924,9 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
def get_asset_gl_entry(self, gl_entries):
|
||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||
arbnb_account = None
|
||||
eiiav_account = None
|
||||
asset_eiiav_currency = None
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.is_fixed_asset:
|
||||
@@ -937,6 +938,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"Asset Received But Not Billed",
|
||||
"Fixed Asset",
|
||||
]:
|
||||
if not arbnb_account:
|
||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
||||
item.expense_account = arbnb_account
|
||||
|
||||
if not self.update_stock:
|
||||
@@ -959,7 +962,10 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
if item.item_tax_amount:
|
||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||
if not eiiav_account or not asset_eiiav_currency:
|
||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
@@ -1002,7 +1008,10 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||
if not eiiav_account or not asset_eiiav_currency:
|
||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
@@ -1022,47 +1031,46 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
)
|
||||
|
||||
# When update stock is checked
|
||||
# Assets are bought through this document then it will be linked to this document
|
||||
if self.update_stock:
|
||||
if flt(item.landed_cost_voucher_amount):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": eiiav_account,
|
||||
"against": cwip_account,
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project or self.project,
|
||||
},
|
||||
item=item,
|
||||
)
|
||||
)
|
||||
if flt(item.landed_cost_voucher_amount):
|
||||
if not eiiav_account:
|
||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": cwip_account,
|
||||
"against": eiiav_account,
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project or self.project,
|
||||
},
|
||||
item=item,
|
||||
)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": eiiav_account,
|
||||
"against": cwip_account,
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project or self.project,
|
||||
},
|
||||
item=item,
|
||||
)
|
||||
|
||||
# update gross amount of assets bought through this document
|
||||
assets = frappe.db.get_all(
|
||||
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
||||
)
|
||||
for asset in assets:
|
||||
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||
frappe.db.set_value(
|
||||
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": cwip_account,
|
||||
"against": eiiav_account,
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project or self.project,
|
||||
},
|
||||
item=item,
|
||||
)
|
||||
)
|
||||
|
||||
# update gross amount of assets bought through this document
|
||||
assets = frappe.db.get_all(
|
||||
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
||||
)
|
||||
for asset in assets:
|
||||
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
|
||||
|
||||
return gl_entries
|
||||
|
||||
|
||||
@@ -1580,6 +1580,76 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
||||
|
||||
self.assertTrue(return_pi.docstatus == 1)
|
||||
|
||||
def test_payment_allocation_for_payment_terms(self):
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
|
||||
create_pr_against_po,
|
||||
create_purchase_order,
|
||||
)
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||
automatically_fetch_payment_terms,
|
||||
)
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||
make_purchase_invoice as make_pi_from_pr,
|
||||
)
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
frappe.db.set_value(
|
||||
"Payment Terms Template",
|
||||
"_Test Payment Term Template",
|
||||
"allocate_payment_based_on_payment_terms",
|
||||
0,
|
||||
)
|
||||
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.payment_terms_template = "_Test Payment Term Template"
|
||||
po.save()
|
||||
po.submit()
|
||||
|
||||
pr = create_pr_against_po(po.name, received_qty=4)
|
||||
pi = make_pi_from_pr(pr.name)
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Payment Terms Template",
|
||||
"_Test Payment Term Template",
|
||||
"allocate_payment_based_on_payment_terms",
|
||||
1,
|
||||
)
|
||||
pi = make_pi_from_pr(pr.name)
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500)
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
frappe.db.set_value(
|
||||
"Payment Terms Template",
|
||||
"_Test Payment Term Template",
|
||||
"allocate_payment_based_on_payment_terms",
|
||||
0,
|
||||
)
|
||||
|
||||
def test_default_cost_center_for_purchase(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
|
||||
for c_center in ["_Test Cost Center Selling", "_Test Cost Center Buying"]:
|
||||
create_cost_center(cost_center_name=c_center)
|
||||
|
||||
item = create_item(
|
||||
"_Test Cost Center Item For Purchase",
|
||||
is_stock_item=1,
|
||||
buying_cost_center="_Test Cost Center Buying - _TC",
|
||||
selling_cost_center="_Test Cost Center Selling - _TC",
|
||||
)
|
||||
|
||||
pi = make_purchase_invoice(
|
||||
item=item.name, qty=1, rate=1000, update_stock=True, do_not_submit=True, cost_center=""
|
||||
)
|
||||
|
||||
pi.items[0].cost_center = ""
|
||||
pi.set_missing_values()
|
||||
pi.calculate_taxes_and_totals()
|
||||
pi.save()
|
||||
|
||||
self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC")
|
||||
|
||||
|
||||
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
||||
gl_entries = frappe.db.sql(
|
||||
|
||||
@@ -1580,15 +1580,13 @@ class SalesInvoice(SellingController):
|
||||
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
|
||||
|
||||
def get_returned_amount(self):
|
||||
from frappe.query_builder.functions import Coalesce, Sum
|
||||
from frappe.query_builder.functions import Sum
|
||||
|
||||
doc = frappe.qb.DocType(self.doctype)
|
||||
returned_amount = (
|
||||
frappe.qb.from_(doc)
|
||||
.select(Sum(doc.grand_total))
|
||||
.where(
|
||||
(doc.docstatus == 1) & (doc.is_return == 1) & (Coalesce(doc.return_against, "") == self.name)
|
||||
)
|
||||
.where((doc.docstatus == 1) & (doc.is_return == 1) & (doc.return_against == self.name))
|
||||
).run()
|
||||
|
||||
return abs(returned_amount[0][0]) if returned_amount[0][0] else 0
|
||||
|
||||
@@ -1783,6 +1783,10 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
||||
"""Test impact of advance PE submission/cancellation on SI and SO."""
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
sales_order = make_sales_order(item_code="138-CMS Shoe", qty=1, price_list_rate=500)
|
||||
pe = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Payment Entry",
|
||||
@@ -1802,10 +1806,25 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"paid_to": "_Test Cash - _TC",
|
||||
}
|
||||
)
|
||||
pe.append(
|
||||
"references",
|
||||
{
|
||||
"reference_doctype": "Sales Order",
|
||||
"reference_name": sales_order.name,
|
||||
"total_amount": sales_order.grand_total,
|
||||
"outstanding_amount": sales_order.grand_total,
|
||||
"allocated_amount": 300,
|
||||
},
|
||||
)
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
sales_order.reload()
|
||||
self.assertEqual(sales_order.advance_paid, 300)
|
||||
|
||||
si = frappe.copy_doc(test_records[0])
|
||||
si.items[0].sales_order = sales_order.name
|
||||
si.items[0].so_detail = sales_order.get("items")[0].name
|
||||
si.is_pos = 0
|
||||
si.append(
|
||||
"advances",
|
||||
@@ -1813,6 +1832,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"doctype": "Sales Invoice Advance",
|
||||
"reference_type": "Payment Entry",
|
||||
"reference_name": pe.name,
|
||||
"reference_row": pe.references[0].name,
|
||||
"advance_amount": 300,
|
||||
"allocated_amount": 300,
|
||||
"remarks": pe.remarks,
|
||||
@@ -1821,7 +1841,13 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
si.load_from_db()
|
||||
si.reload()
|
||||
pe.reload()
|
||||
sales_order.reload()
|
||||
|
||||
# Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0
|
||||
self.assertEqual(pe.references[0].reference_name, si.name)
|
||||
self.assertEqual(sales_order.advance_paid, 0.0)
|
||||
|
||||
# check outstanding after advance allocation
|
||||
self.assertEqual(
|
||||
@@ -1829,11 +1855,9 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
|
||||
)
|
||||
|
||||
# added to avoid Document has been modified exception
|
||||
pe = frappe.get_doc("Payment Entry", pe.name)
|
||||
pe.cancel()
|
||||
si.reload()
|
||||
|
||||
si.load_from_db()
|
||||
# check outstanding after advance cancellation
|
||||
self.assertEqual(
|
||||
flt(si.outstanding_amount),
|
||||
@@ -2448,36 +2472,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(target_doc.company, "_Test Company 1")
|
||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||
|
||||
def test_sle_for_target_warehouse(self):
|
||||
se = make_stock_entry(
|
||||
item_code="138-CMS Shoe",
|
||||
target="Finished Goods - _TC",
|
||||
company="_Test Company",
|
||||
qty=1,
|
||||
basic_rate=500,
|
||||
)
|
||||
|
||||
si = frappe.copy_doc(test_records[0])
|
||||
si.update_stock = 1
|
||||
si.set_warehouse = "Finished Goods - _TC"
|
||||
si.set_target_warehouse = "Stores - _TC"
|
||||
si.get("items")[0].warehouse = "Finished Goods - _TC"
|
||||
si.get("items")[0].target_warehouse = "Stores - _TC"
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
sles = frappe.get_all(
|
||||
"Stock Ledger Entry", filters={"voucher_no": si.name}, fields=["name", "actual_qty"]
|
||||
)
|
||||
|
||||
# check if both SLEs are created
|
||||
self.assertEqual(len(sles), 2)
|
||||
self.assertEqual(sum(d.actual_qty for d in sles), 0.0)
|
||||
|
||||
# tear down
|
||||
si.cancel()
|
||||
se.cancel()
|
||||
|
||||
def test_internal_transfer_gl_entry(self):
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company with perpetual inventory",
|
||||
|
||||
@@ -694,3 +694,23 @@ class TestSubscription(unittest.TestCase):
|
||||
# Check the currency of the created invoice
|
||||
currency = frappe.db.get_value("Sales Invoice", subscription.invoices[0].invoice, "currency")
|
||||
self.assertEqual(currency, "USD")
|
||||
|
||||
def test_plan_rate_for_midmonth_start_date(self):
|
||||
subscription = frappe.new_doc("Subscription")
|
||||
subscription.party_type = "Supplier"
|
||||
subscription.party = "_Test Supplier"
|
||||
subscription.generate_invoice_at_period_start = 1
|
||||
subscription.follow_calendar_months = 1
|
||||
subscription.generate_new_invoices_past_due_date = 1
|
||||
subscription.start_date = "2023-04-08"
|
||||
subscription.end_date = "2024-02-27"
|
||||
subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1})
|
||||
subscription.save()
|
||||
|
||||
subscription.process()
|
||||
|
||||
self.assertEqual(len(subscription.invoices), 1)
|
||||
pi = frappe.get_doc("Purchase Invoice", subscription.invoices[0].invoice)
|
||||
self.assertEqual(pi.total, 55333.33)
|
||||
|
||||
subscription.delete()
|
||||
|
||||
@@ -56,18 +56,17 @@ def get_plan_rate(
|
||||
prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
|
||||
|
||||
if prorate:
|
||||
prorate_factor = flt(
|
||||
date_diff(start_date, get_first_day(start_date))
|
||||
/ date_diff(get_last_day(start_date), get_first_day(start_date)),
|
||||
1,
|
||||
)
|
||||
|
||||
prorate_factor += flt(
|
||||
date_diff(get_last_day(end_date), end_date)
|
||||
/ date_diff(get_last_day(end_date), get_first_day(end_date)),
|
||||
1,
|
||||
)
|
||||
|
||||
cost -= plan.cost * prorate_factor
|
||||
|
||||
cost -= plan.cost * get_prorate_factor(start_date, end_date)
|
||||
return cost
|
||||
|
||||
|
||||
def get_prorate_factor(start_date, end_date):
|
||||
total_days_to_skip = date_diff(start_date, get_first_day(start_date))
|
||||
total_days_in_month = int(get_last_day(start_date).strftime("%d"))
|
||||
prorate_factor = flt(total_days_to_skip / total_days_in_month)
|
||||
|
||||
total_days_to_skip = date_diff(get_last_day(end_date), end_date)
|
||||
total_days_in_month = int(get_last_day(end_date).strftime("%d"))
|
||||
prorate_factor += flt(total_days_to_skip / total_days_in_month)
|
||||
|
||||
return prorate_factor
|
||||
|
||||
@@ -186,6 +186,42 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_tds_deduction_for_po_via_payment_entry(self):
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier8", "tax_withholding_category", "Cumulative Threshold TDS"
|
||||
)
|
||||
order = create_purchase_order(supplier="Test TDS Supplier8", rate=40000, do_not_save=True)
|
||||
|
||||
# Add some tax on the order
|
||||
order.append(
|
||||
"taxes",
|
||||
{
|
||||
"category": "Total",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"tax_amount": 8000,
|
||||
"description": "Test",
|
||||
"add_deduct_tax": "Add",
|
||||
},
|
||||
)
|
||||
|
||||
order.save()
|
||||
|
||||
order.apply_tds = 1
|
||||
order.tax_withholding_category = "Cumulative Threshold TDS"
|
||||
order.submit()
|
||||
|
||||
self.assertEqual(order.taxes[0].tax_amount, 4000)
|
||||
|
||||
payment = get_payment_entry(order.doctype, order.name)
|
||||
payment.apply_tax_withholding_amount = 1
|
||||
payment.tax_withholding_category = "Cumulative Threshold TDS"
|
||||
payment.submit()
|
||||
self.assertEqual(payment.taxes[0].tax_amount, 4000)
|
||||
|
||||
def test_multi_category_single_supplier(self):
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
|
||||
@@ -275,6 +311,37 @@ def cancel_invoices():
|
||||
frappe.get_doc("Sales Invoice", d).cancel()
|
||||
|
||||
|
||||
def create_purchase_order(**args):
|
||||
# return purchase order doc object
|
||||
item = frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name")
|
||||
args = frappe._dict(args)
|
||||
po = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Purchase Order",
|
||||
"transaction_date": today(),
|
||||
"schedule_date": today(),
|
||||
"apply_tds": 0 if args.do_not_apply_tds else 1,
|
||||
"supplier": args.supplier,
|
||||
"company": "_Test Company",
|
||||
"taxes_and_charges": "",
|
||||
"currency": "INR",
|
||||
"taxes": [],
|
||||
"items": [
|
||||
{
|
||||
"doctype": "Purchase Order Item",
|
||||
"item_code": item,
|
||||
"qty": args.qty or 1,
|
||||
"rate": args.rate or 10000,
|
||||
"cost_center": "Main - _TC",
|
||||
"expense_account": "Stock Received But Not Billed - _TC",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
po.save()
|
||||
return po
|
||||
|
||||
|
||||
def create_purchase_invoice(**args):
|
||||
# return sales invoice doc object
|
||||
item = frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name")
|
||||
@@ -351,6 +418,8 @@ def create_records():
|
||||
"Test TDS Supplier4",
|
||||
"Test TDS Supplier5",
|
||||
"Test TDS Supplier6",
|
||||
"Test TDS Supplier7",
|
||||
"Test TDS Supplier8",
|
||||
]:
|
||||
if frappe.db.exists("Supplier", name):
|
||||
continue
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _, msgprint, scrub
|
||||
from frappe.contacts.doctype.address.address import (
|
||||
get_address_display,
|
||||
get_company_address,
|
||||
get_default_address,
|
||||
)
|
||||
from frappe.contacts.doctype.contact.contact import get_contact_details
|
||||
from frappe.contacts.doctype.address.address import get_company_address, get_default_address
|
||||
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import (
|
||||
@@ -120,6 +115,7 @@ def _get_party_details(
|
||||
party_address,
|
||||
company_address,
|
||||
shipping_address,
|
||||
ignore_permissions=ignore_permissions,
|
||||
)
|
||||
set_contact_details(party_details, party, party_type)
|
||||
set_other_values(party_details, party, party_type)
|
||||
@@ -183,6 +179,8 @@ def set_address_details(
|
||||
party_address=None,
|
||||
company_address=None,
|
||||
shipping_address=None,
|
||||
*,
|
||||
ignore_permissions=False
|
||||
):
|
||||
billing_address_field = (
|
||||
"customer_address" if party_type == "Lead" else party_type.lower() + "_address"
|
||||
@@ -195,13 +193,17 @@ def set_address_details(
|
||||
get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])
|
||||
)
|
||||
# address display
|
||||
party_details.address_display = get_address_display(party_details[billing_address_field])
|
||||
party_details.address_display = render_address(
|
||||
party_details[billing_address_field], check_permissions=not ignore_permissions
|
||||
)
|
||||
# shipping address
|
||||
if party_type in ["Customer", "Lead"]:
|
||||
party_details.shipping_address_name = shipping_address or get_party_shipping_address(
|
||||
party_type, party.name
|
||||
)
|
||||
party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
|
||||
party_details.shipping_address = render_address(
|
||||
party_details["shipping_address_name"], check_permissions=not ignore_permissions
|
||||
)
|
||||
if doctype:
|
||||
party_details.update(
|
||||
get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name)
|
||||
@@ -224,7 +226,7 @@ def set_address_details(
|
||||
party_details.update(
|
||||
{
|
||||
"shipping_address": shipping_address,
|
||||
"shipping_address_display": get_address_display(shipping_address),
|
||||
"shipping_address_display": render_address(shipping_address),
|
||||
**get_fetch_values(doctype, "shipping_address", shipping_address),
|
||||
}
|
||||
)
|
||||
@@ -235,7 +237,8 @@ def set_address_details(
|
||||
{
|
||||
"billing_address": party_details.company_address,
|
||||
"billing_address_display": (
|
||||
party_details.company_address_display or get_address_display(party_details.company_address)
|
||||
party_details.company_address_display
|
||||
or render_address(party_details.company_address, check_permissions=True)
|
||||
),
|
||||
**get_fetch_values(doctype, "billing_address", party_details.company_address),
|
||||
}
|
||||
@@ -277,7 +280,34 @@ def set_contact_details(party_details, party, party_type):
|
||||
}
|
||||
)
|
||||
else:
|
||||
party_details.update(get_contact_details(party_details.contact_person))
|
||||
fields = [
|
||||
"name as contact_person",
|
||||
"salutation",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email_id as contact_email",
|
||||
"mobile_no as contact_mobile",
|
||||
"phone as contact_phone",
|
||||
"designation as contact_designation",
|
||||
"department as contact_department",
|
||||
]
|
||||
|
||||
contact_details = frappe.db.get_value(
|
||||
"Contact", party_details.contact_person, fields, as_dict=True
|
||||
)
|
||||
|
||||
contact_details.contact_display = " ".join(
|
||||
filter(
|
||||
None,
|
||||
[
|
||||
contact_details.get("salutation"),
|
||||
contact_details.get("first_name"),
|
||||
contact_details.get("last_name"),
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
party_details.update(contact_details)
|
||||
|
||||
|
||||
def set_other_values(party_details, party, party_type):
|
||||
@@ -938,3 +968,13 @@ def add_party_account(party_type, party, company, account):
|
||||
doc.append("accounts", accounts)
|
||||
|
||||
doc.save()
|
||||
|
||||
|
||||
def render_address(address, check_permissions=True):
|
||||
try:
|
||||
from frappe.contacts.doctype.address.address import render_address as _render
|
||||
except ImportError:
|
||||
# Older frappe versions where this function is not available
|
||||
from frappe.contacts.doctype.address.address import get_address_display as _render
|
||||
|
||||
return frappe.call(_render, address, check_permissions=check_permissions)
|
||||
|
||||
@@ -457,6 +457,8 @@ class GrossProfitGenerator(object):
|
||||
new_row.qty += flt(row.qty)
|
||||
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
|
||||
new_row.base_amount += flt(row.base_amount, self.currency_precision)
|
||||
if self.filters.get("group_by") == "Sales Person":
|
||||
new_row.allocated_amount += flt(row.allocated_amount, self.currency_precision)
|
||||
new_row = self.set_average_rate(new_row)
|
||||
self.grouped_data.append(new_row)
|
||||
else:
|
||||
|
||||
@@ -15,6 +15,7 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i
|
||||
get_group_by_conditions,
|
||||
get_tax_accounts,
|
||||
)
|
||||
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
@@ -39,16 +40,6 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
tax_doctype="Purchase Taxes and Charges",
|
||||
)
|
||||
|
||||
scrubbed_tax_fields = {}
|
||||
|
||||
for tax in tax_columns:
|
||||
scrubbed_tax_fields.update(
|
||||
{
|
||||
tax + " Rate": frappe.scrub(tax + " Rate"),
|
||||
tax + " Amount": frappe.scrub(tax + " Amount"),
|
||||
}
|
||||
)
|
||||
|
||||
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
|
||||
|
||||
data = []
|
||||
@@ -59,7 +50,11 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if filters.get("group_by"):
|
||||
grand_total = get_grand_total(filters, "Purchase Invoice")
|
||||
|
||||
item_details = get_item_details()
|
||||
|
||||
for d in item_list:
|
||||
item_record = item_details.get(d.item_code)
|
||||
|
||||
purchase_receipt = None
|
||||
if d.purchase_receipt:
|
||||
purchase_receipt = d.purchase_receipt
|
||||
@@ -72,8 +67,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
"item_code": d.item_code,
|
||||
"item_name": d.pi_item_name if d.pi_item_name else d.i_item_name,
|
||||
"item_group": d.pi_item_group if d.pi_item_group else d.i_item_group,
|
||||
"item_name": item_record.item_name if item_record else d.item_name,
|
||||
"item_group": item_record.item_group if item_record else d.item_group,
|
||||
"description": d.description,
|
||||
"invoice": d.parent,
|
||||
"posting_date": d.posting_date,
|
||||
@@ -106,8 +101,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||
row.update(
|
||||
{
|
||||
scrubbed_tax_fields[tax + " Rate"]: item_tax.get("tax_rate", 0),
|
||||
scrubbed_tax_fields[tax + " Amount"]: item_tax.get("tax_amount", 0),
|
||||
frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0),
|
||||
frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0),
|
||||
}
|
||||
)
|
||||
total_tax += flt(item_tax.get("tax_amount"))
|
||||
@@ -330,17 +325,15 @@ def get_items(filters, additional_query_columns):
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||
`tabPurchase Invoice`.unrealized_profit_loss_account,
|
||||
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` as pi_item_group,
|
||||
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
|
||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
|
||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||
`tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`,
|
||||
`tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {0}
|
||||
from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem`
|
||||
from `tabPurchase Invoice`, `tabPurchase Invoice Item`
|
||||
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
|
||||
`tabItem`.name = `tabPurchase Invoice Item`.`item_code` and
|
||||
`tabPurchase Invoice`.docstatus = 1 %s
|
||||
`tabPurchase Invoice`.docstatus = 1 %s
|
||||
""".format(
|
||||
additional_query_columns
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ from frappe.utils.xlsxutils import handle_html
|
||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import (
|
||||
get_customer_details,
|
||||
get_item_details,
|
||||
)
|
||||
|
||||
|
||||
@@ -34,16 +35,6 @@ def _execute(
|
||||
if item_list:
|
||||
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
|
||||
|
||||
scrubbed_tax_fields = {}
|
||||
|
||||
for tax in tax_columns:
|
||||
scrubbed_tax_fields.update(
|
||||
{
|
||||
tax + " Rate": frappe.scrub(tax + " Rate"),
|
||||
tax + " Amount": frappe.scrub(tax + " Amount"),
|
||||
}
|
||||
)
|
||||
|
||||
mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list))
|
||||
so_dn_map = get_delivery_notes_against_sales_order(item_list)
|
||||
|
||||
@@ -56,9 +47,11 @@ def _execute(
|
||||
grand_total = get_grand_total(filters, "Sales Invoice")
|
||||
|
||||
customer_details = get_customer_details()
|
||||
item_details = get_item_details()
|
||||
|
||||
for d in item_list:
|
||||
customer_record = customer_details.get(d.customer)
|
||||
item_record = item_details.get(d.item_code)
|
||||
|
||||
delivery_note = None
|
||||
if d.delivery_note:
|
||||
@@ -71,8 +64,8 @@ def _execute(
|
||||
|
||||
row = {
|
||||
"item_code": d.item_code,
|
||||
"item_name": d.si_item_name if d.si_item_name else d.i_item_name,
|
||||
"item_group": d.si_item_group if d.si_item_group else d.i_item_group,
|
||||
"item_name": item_record.item_name if item_record else d.item_name,
|
||||
"item_group": item_record.item_group if item_record else d.item_group,
|
||||
"description": d.description,
|
||||
"invoice": d.parent,
|
||||
"posting_date": d.posting_date,
|
||||
@@ -114,8 +107,8 @@ def _execute(
|
||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||
row.update(
|
||||
{
|
||||
scrubbed_tax_fields[tax + " Rate"]: item_tax.get("tax_rate", 0),
|
||||
scrubbed_tax_fields[tax + " Amount"]: item_tax.get("tax_amount", 0),
|
||||
frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0),
|
||||
frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0),
|
||||
}
|
||||
)
|
||||
if item_tax.get("is_other_charges"):
|
||||
@@ -411,18 +404,15 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
|
||||
`tabSales Invoice Item`.project,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||
`tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`,
|
||||
`tabSales Invoice Item`.`item_name` as si_item_name, `tabSales Invoice Item`.`item_group` as si_item_group,
|
||||
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
|
||||
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
|
||||
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
|
||||
from `tabSales Invoice`, `tabSales Invoice Item`, `tabItem`
|
||||
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and
|
||||
`tabItem`.name = `tabSales Invoice Item`.`item_code` and
|
||||
`tabSales Invoice`.docstatus = 1 {1}
|
||||
from `tabSales Invoice`, `tabSales Invoice Item`
|
||||
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
||||
and `tabSales Invoice`.docstatus = 1 {1}
|
||||
""".format(
|
||||
additional_query_columns or "", conditions
|
||||
),
|
||||
|
||||
@@ -537,6 +537,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||
"""
|
||||
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||
|
||||
# Update Advance Paid in SO/PO since they might be getting unlinked
|
||||
if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
|
||||
frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
|
||||
|
||||
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
|
||||
# adjust the unreconciled balance
|
||||
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
|
||||
@@ -596,6 +600,13 @@ def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
|
||||
|
||||
if d.voucher_detail_no:
|
||||
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
||||
|
||||
# Update Advance Paid in SO/PO since they are getting unlinked
|
||||
if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"):
|
||||
frappe.get_doc(
|
||||
existing_row.reference_doctype, existing_row.reference_name
|
||||
).set_total_advance_paid()
|
||||
|
||||
original_row = existing_row.as_dict().copy()
|
||||
existing_row.update(reference_details)
|
||||
|
||||
@@ -810,7 +821,7 @@ def get_held_invoices(party_type, party):
|
||||
|
||||
if party_type == "Supplier":
|
||||
held_invoices = frappe.db.sql(
|
||||
"select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()",
|
||||
"select name from `tabPurchase Invoice` where on_hold = 1 and release_date IS NOT NULL and release_date > CURDATE()",
|
||||
as_dict=1,
|
||||
)
|
||||
held_invoices = set(d["name"] for d in held_invoices)
|
||||
|
||||
@@ -41,6 +41,7 @@ class Asset(AccountsController):
|
||||
self.validate_item()
|
||||
self.validate_cost_center()
|
||||
self.set_missing_values()
|
||||
self.validate_finance_books()
|
||||
self.prepare_depreciation_data()
|
||||
self.validate_gross_and_purchase_amount()
|
||||
if self.get("schedules"):
|
||||
@@ -148,17 +149,33 @@ class Asset(AccountsController):
|
||||
frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code))
|
||||
|
||||
def validate_cost_center(self):
|
||||
if not self.cost_center:
|
||||
return
|
||||
|
||||
cost_center_company = frappe.db.get_value("Cost Center", self.cost_center, "company")
|
||||
if cost_center_company != self.company:
|
||||
frappe.throw(
|
||||
_("Selected Cost Center {} doesn't belongs to {}").format(
|
||||
frappe.bold(self.cost_center), frappe.bold(self.company)
|
||||
),
|
||||
title=_("Invalid Cost Center"),
|
||||
if self.cost_center:
|
||||
cost_center_company, cost_center_is_group = frappe.db.get_value(
|
||||
"Cost Center", self.cost_center, ["company", "is_group"]
|
||||
)
|
||||
if cost_center_company != self.company:
|
||||
frappe.throw(
|
||||
_("Cost Center {} doesn't belong to Company {}").format(
|
||||
frappe.bold(self.cost_center), frappe.bold(self.company)
|
||||
),
|
||||
title=_("Invalid Cost Center"),
|
||||
)
|
||||
if cost_center_is_group:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Cost Center {} is a group cost center and group cost centers cannot be used in transactions"
|
||||
).format(frappe.bold(self.cost_center)),
|
||||
title=_("Invalid Cost Center"),
|
||||
)
|
||||
|
||||
else:
|
||||
if not frappe.get_cached_value("Company", self.company, "depreciation_cost_center"):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Please set a Cost Center for the Asset or set an Asset Depreciation Cost Center for the Company {}"
|
||||
).format(frappe.bold(self.company)),
|
||||
title=_("Missing Cost Center"),
|
||||
)
|
||||
|
||||
def validate_in_use_date(self):
|
||||
if not self.available_for_use_date:
|
||||
@@ -181,6 +198,27 @@ class Asset(AccountsController):
|
||||
finance_books = get_item_details(self.item_code, self.asset_category)
|
||||
self.set("finance_books", finance_books)
|
||||
|
||||
def validate_finance_books(self):
|
||||
if not self.calculate_depreciation or len(self.finance_books) == 1:
|
||||
return
|
||||
|
||||
finance_books = set()
|
||||
|
||||
for d in self.finance_books:
|
||||
if d.finance_book in finance_books:
|
||||
frappe.throw(
|
||||
_("Row #{}: Please use a different Finance Book.").format(d.idx),
|
||||
title=_("Duplicate Finance Book"),
|
||||
)
|
||||
else:
|
||||
finance_books.add(d.finance_book)
|
||||
|
||||
if not d.finance_book:
|
||||
frappe.throw(
|
||||
_("Row #{}: Finance Book should not be empty since you're using multiple.").format(d.idx),
|
||||
title=_("Missing Finance Book"),
|
||||
)
|
||||
|
||||
def validate_asset_values(self):
|
||||
if not self.asset_category:
|
||||
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
|
||||
|
||||
@@ -33,6 +33,7 @@ def post_depreciation_entries(date=None):
|
||||
date = today()
|
||||
|
||||
failed_asset_names = []
|
||||
error_log_names = []
|
||||
|
||||
for asset_name in get_depreciable_assets(date):
|
||||
try:
|
||||
@@ -41,10 +42,12 @@ def post_depreciation_entries(date=None):
|
||||
except Exception as e:
|
||||
frappe.db.rollback()
|
||||
failed_asset_names.append(asset_name)
|
||||
error_log = frappe.log_error(e)
|
||||
error_log_names.append(error_log.name)
|
||||
|
||||
if failed_asset_names:
|
||||
set_depr_entry_posting_status_for_failed_assets(failed_asset_names)
|
||||
notify_depr_entry_posting_error(failed_asset_names)
|
||||
notify_depr_entry_posting_error(failed_asset_names, error_log_names)
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
@@ -216,7 +219,7 @@ def set_depr_entry_posting_status_for_failed_assets(failed_asset_names):
|
||||
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed")
|
||||
|
||||
|
||||
def notify_depr_entry_posting_error(failed_asset_names):
|
||||
def notify_depr_entry_posting_error(failed_asset_names, error_log_names):
|
||||
recipients = get_users_with_role("Accounts Manager")
|
||||
|
||||
if not recipients:
|
||||
@@ -224,7 +227,8 @@ def notify_depr_entry_posting_error(failed_asset_names):
|
||||
|
||||
subject = _("Error while posting depreciation entries")
|
||||
|
||||
asset_links = get_comma_separated_asset_links(failed_asset_names)
|
||||
asset_links = get_comma_separated_links(failed_asset_names, "Asset")
|
||||
error_log_links = get_comma_separated_links(error_log_names, "Error Log")
|
||||
|
||||
message = (
|
||||
_("Hello,")
|
||||
@@ -234,23 +238,26 @@ def notify_depr_entry_posting_error(failed_asset_names):
|
||||
)
|
||||
+ "."
|
||||
+ "<br><br>"
|
||||
+ _(
|
||||
"Please raise a support ticket and share this email, or forward this email to your development team so that they can find the issue in the developer console by manually creating the depreciation entry via the asset's depreciation schedule table."
|
||||
+ _("Here are the error logs for the aforementioned failed depreciation entries: {0}").format(
|
||||
error_log_links
|
||||
)
|
||||
+ "."
|
||||
+ "<br><br>"
|
||||
+ _("Please share this email with your support team so that they can find and fix the issue.")
|
||||
)
|
||||
|
||||
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
||||
|
||||
|
||||
def get_comma_separated_asset_links(asset_names):
|
||||
asset_links = []
|
||||
def get_comma_separated_links(names, doctype):
|
||||
links = []
|
||||
|
||||
for asset_name in asset_names:
|
||||
asset_links.append(get_link_to_form("Asset", asset_name))
|
||||
for name in names:
|
||||
links.append(get_link_to_form(doctype, name))
|
||||
|
||||
asset_links = ", ".join(asset_links)
|
||||
links = ", ".join(links)
|
||||
|
||||
return asset_links
|
||||
return links
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -1287,6 +1287,7 @@ class TestDepreciationBasics(AssetSetup):
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"finance_book": "Test Finance Book 1",
|
||||
"depreciation_method": "Straight Line",
|
||||
"frequency_of_depreciation": 1,
|
||||
"total_number_of_depreciations": 3,
|
||||
@@ -1297,6 +1298,7 @@ class TestDepreciationBasics(AssetSetup):
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"finance_book": "Test Finance Book 2",
|
||||
"depreciation_method": "Straight Line",
|
||||
"frequency_of_depreciation": 1,
|
||||
"total_number_of_depreciations": 6,
|
||||
@@ -1307,6 +1309,7 @@ class TestDepreciationBasics(AssetSetup):
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"finance_book": "Test Finance Book 3",
|
||||
"depreciation_method": "Straight Line",
|
||||
"frequency_of_depreciation": 12,
|
||||
"total_number_of_depreciations": 3,
|
||||
@@ -1336,6 +1339,7 @@ class TestDepreciationBasics(AssetSetup):
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"finance_book": "Test Finance Book 1",
|
||||
"depreciation_method": "Straight Line",
|
||||
"frequency_of_depreciation": 12,
|
||||
"total_number_of_depreciations": 3,
|
||||
@@ -1346,6 +1350,7 @@ class TestDepreciationBasics(AssetSetup):
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"finance_book": "Test Finance Book 2",
|
||||
"depreciation_method": "Straight Line",
|
||||
"frequency_of_depreciation": 12,
|
||||
"total_number_of_depreciations": 6,
|
||||
@@ -1581,6 +1586,15 @@ def create_asset_data():
|
||||
if not frappe.db.exists("Location", "Test Location"):
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
|
||||
|
||||
if not frappe.db.exists("Finance Book", "Test Finance Book 1"):
|
||||
frappe.get_doc({"doctype": "Finance Book", "finance_book_name": "Test Finance Book 1"}).insert()
|
||||
|
||||
if not frappe.db.exists("Finance Book", "Test Finance Book 2"):
|
||||
frappe.get_doc({"doctype": "Finance Book", "finance_book_name": "Test Finance Book 2"}).insert()
|
||||
|
||||
if not frappe.db.exists("Finance Book", "Test Finance Book 3"):
|
||||
frappe.get_doc({"doctype": "Finance Book", "finance_book_name": "Test Finance Book 3"}).insert()
|
||||
|
||||
|
||||
def create_asset(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -33,6 +33,7 @@ frappe.ui.form.on('Asset Category', {
|
||||
var d = locals[cdt][cdn];
|
||||
return {
|
||||
"filters": {
|
||||
"account_type": "Depreciation",
|
||||
"root_type": ["in", ["Expense", "Income"]],
|
||||
"is_group": 0,
|
||||
"company": d.company_name
|
||||
|
||||
@@ -53,7 +53,7 @@ class AssetCategory(Document):
|
||||
account_type_map = {
|
||||
"fixed_asset_account": {"account_type": ["Fixed Asset"]},
|
||||
"accumulated_depreciation_account": {"account_type": ["Accumulated Depreciation"]},
|
||||
"depreciation_expense_account": {"root_type": ["Expense", "Income"]},
|
||||
"depreciation_expense_account": {"account_type": ["Depreciation"]},
|
||||
"capital_work_in_progress_account": {"account_type": ["Capital Work in Progress"]},
|
||||
}
|
||||
for d in self.accounts:
|
||||
|
||||
@@ -63,7 +63,7 @@ frappe.ui.form.on('Asset Movement', {
|
||||
fieldnames_to_be_altered = {
|
||||
target_location: { read_only: 0, reqd: 1 },
|
||||
source_location: { read_only: 1, reqd: 0 },
|
||||
from_employee: { read_only: 0, reqd: 1 },
|
||||
from_employee: { read_only: 0, reqd: 0 },
|
||||
to_employee: { read_only: 1, reqd: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,28 +63,19 @@ class AssetMovement(Document):
|
||||
frappe.throw(_("Source and Target Location cannot be same"))
|
||||
|
||||
if self.purpose == "Receipt":
|
||||
# only when asset is bought and first entry is made
|
||||
if not d.source_location and not (d.target_location or d.to_employee):
|
||||
if not (d.source_location) and not (d.target_location or d.to_employee):
|
||||
frappe.throw(
|
||||
_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
|
||||
)
|
||||
elif d.source_location:
|
||||
# when asset is received from an employee
|
||||
if d.target_location and not d.from_employee:
|
||||
frappe.throw(
|
||||
_("From employee is required while receiving Asset {0} to a target location").format(
|
||||
d.asset
|
||||
)
|
||||
)
|
||||
if d.from_employee and not d.target_location:
|
||||
frappe.throw(
|
||||
_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
|
||||
)
|
||||
if d.to_employee and d.target_location:
|
||||
elif d.to_employee and d.target_location:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Asset {0} cannot be received at a location and \
|
||||
given to employee in a single movement"
|
||||
"Asset {0} cannot be received at a location and given to an employee in a single movement"
|
||||
).format(d.asset)
|
||||
)
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@ from itertools import chain
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import IfNull, Sum
|
||||
from frappe.utils import cstr, flt, formatdate, getdate
|
||||
from frappe.utils import add_months, cstr, flt, formatdate, getdate, nowdate, today
|
||||
|
||||
from erpnext.accounts.report.financial_statements import (
|
||||
get_fiscal_year_data,
|
||||
get_period_list,
|
||||
validate_fiscal_year,
|
||||
)
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciation
|
||||
|
||||
|
||||
@@ -37,15 +38,26 @@ def get_conditions(filters):
|
||||
|
||||
if filters.get("company"):
|
||||
conditions["company"] = filters.company
|
||||
|
||||
if filters.filter_based_on == "Date Range":
|
||||
if not filters.from_date and not filters.to_date:
|
||||
filters.from_date = add_months(nowdate(), -12)
|
||||
filters.to_date = nowdate()
|
||||
|
||||
conditions[date_field] = ["between", [filters.from_date, filters.to_date]]
|
||||
if filters.filter_based_on == "Fiscal Year":
|
||||
elif filters.filter_based_on == "Fiscal Year":
|
||||
if not filters.from_fiscal_year and not filters.to_fiscal_year:
|
||||
default_fiscal_year = get_fiscal_year(today())[0]
|
||||
filters.from_fiscal_year = default_fiscal_year
|
||||
filters.to_fiscal_year = default_fiscal_year
|
||||
|
||||
fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year)
|
||||
validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year)
|
||||
filters.year_start_date = getdate(fiscal_year.year_start_date)
|
||||
filters.year_end_date = getdate(fiscal_year.year_end_date)
|
||||
|
||||
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
|
||||
|
||||
if filters.get("only_existing_assets"):
|
||||
conditions["is_existing_asset"] = filters.get("only_existing_assets")
|
||||
if filters.get("asset_category"):
|
||||
@@ -54,12 +66,12 @@ def get_conditions(filters):
|
||||
conditions["cost_center"] = filters.get("cost_center")
|
||||
|
||||
if status:
|
||||
# In Store assets are those that are not sold or scrapped
|
||||
# In Store assets are those that are not sold or scrapped or capitalized or decapitalized
|
||||
operand = "not in"
|
||||
if status not in "In Location":
|
||||
operand = "in"
|
||||
|
||||
conditions["status"] = (operand, ["Sold", "Scrapped"])
|
||||
conditions["status"] = (operand, ["Sold", "Scrapped", "Capitalized", "Decapitalized"])
|
||||
|
||||
return conditions
|
||||
|
||||
@@ -71,36 +83,6 @@ def get_data(filters):
|
||||
pr_supplier_map = get_purchase_receipt_supplier_map()
|
||||
pi_supplier_map = get_purchase_invoice_supplier_map()
|
||||
|
||||
group_by = frappe.scrub(filters.get("group_by"))
|
||||
|
||||
if group_by == "asset_category":
|
||||
fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by)
|
||||
|
||||
elif group_by == "location":
|
||||
fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by)
|
||||
|
||||
else:
|
||||
fields = [
|
||||
"name as asset_id",
|
||||
"asset_name",
|
||||
"status",
|
||||
"department",
|
||||
"company",
|
||||
"cost_center",
|
||||
"calculate_depreciation",
|
||||
"purchase_receipt",
|
||||
"asset_category",
|
||||
"purchase_date",
|
||||
"gross_purchase_amount",
|
||||
"location",
|
||||
"available_for_use_date",
|
||||
"purchase_invoice",
|
||||
"opening_accumulated_depreciation",
|
||||
]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
assets_linked_to_fb = get_assets_linked_to_fb(filters)
|
||||
|
||||
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
|
||||
@@ -114,6 +96,31 @@ def get_data(filters):
|
||||
|
||||
depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book)
|
||||
|
||||
group_by = frappe.scrub(filters.get("group_by"))
|
||||
|
||||
if group_by in ("asset_category", "location"):
|
||||
data = get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map)
|
||||
return data
|
||||
|
||||
fields = [
|
||||
"name as asset_id",
|
||||
"asset_name",
|
||||
"status",
|
||||
"department",
|
||||
"company",
|
||||
"cost_center",
|
||||
"calculate_depreciation",
|
||||
"purchase_receipt",
|
||||
"asset_category",
|
||||
"purchase_date",
|
||||
"gross_purchase_amount",
|
||||
"location",
|
||||
"available_for_use_date",
|
||||
"purchase_invoice",
|
||||
"opening_accumulated_depreciation",
|
||||
]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
for asset in assets_record:
|
||||
if (
|
||||
assets_linked_to_fb
|
||||
@@ -136,7 +143,7 @@ def get_data(filters):
|
||||
or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": get_depreciation_amount_of_asset(asset, depreciation_amount_map),
|
||||
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
@@ -230,12 +237,11 @@ def get_assets_linked_to_fb(filters):
|
||||
return assets_linked_to_fb
|
||||
|
||||
|
||||
def get_depreciation_amount_of_asset(asset, depreciation_amount_map):
|
||||
return depreciation_amount_map.get(asset.asset_id) or 0.0
|
||||
|
||||
|
||||
def get_asset_depreciation_amount_map(filters, finance_book):
|
||||
date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
||||
start_date = (
|
||||
filters.from_date if filters.filter_based_on == "Date Range" else filters.year_start_date
|
||||
)
|
||||
end_date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
||||
|
||||
asset = frappe.qb.DocType("Asset")
|
||||
gle = frappe.qb.DocType("GL Entry")
|
||||
@@ -256,25 +262,77 @@ def get_asset_depreciation_amount_map(filters, finance_book):
|
||||
)
|
||||
.where(gle.debit != 0)
|
||||
.where(gle.is_cancelled == 0)
|
||||
.where(company.name == filters.company)
|
||||
.where(asset.docstatus == 1)
|
||||
.groupby(asset.name)
|
||||
)
|
||||
|
||||
if filters.only_existing_assets:
|
||||
query = query.where(asset.is_existing_asset == 1)
|
||||
if filters.asset_category:
|
||||
query = query.where(asset.asset_category == filters.asset_category)
|
||||
if filters.cost_center:
|
||||
query = query.where(asset.cost_center == filters.cost_center)
|
||||
if filters.status:
|
||||
if filters.status == "In Location":
|
||||
query = query.where(asset.status.notin(["Sold", "Scrapped", "Capitalized", "Decapitalized"]))
|
||||
else:
|
||||
query = query.where(asset.status.isin(["Sold", "Scrapped", "Capitalized", "Decapitalized"]))
|
||||
if finance_book:
|
||||
query = query.where(
|
||||
(gle.finance_book.isin([cstr(finance_book), ""])) | (gle.finance_book.isnull())
|
||||
)
|
||||
else:
|
||||
query = query.where((gle.finance_book.isin([""])) | (gle.finance_book.isnull()))
|
||||
|
||||
if filters.filter_based_on in ("Date Range", "Fiscal Year"):
|
||||
query = query.where(gle.posting_date <= date)
|
||||
query = query.where(gle.posting_date >= start_date)
|
||||
query = query.where(gle.posting_date <= end_date)
|
||||
|
||||
query = query.groupby(asset.name)
|
||||
|
||||
asset_depr_amount_map = query.run()
|
||||
|
||||
return dict(asset_depr_amount_map)
|
||||
|
||||
|
||||
def get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map):
|
||||
fields = [
|
||||
group_by,
|
||||
"name",
|
||||
"gross_purchase_amount",
|
||||
"opening_accumulated_depreciation",
|
||||
"calculate_depreciation",
|
||||
]
|
||||
assets = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
data = []
|
||||
|
||||
for a in assets:
|
||||
if assets_linked_to_fb and a.calculate_depreciation and a.name not in assets_linked_to_fb:
|
||||
continue
|
||||
|
||||
a["depreciated_amount"] = depreciation_amount_map.get(a["name"], 0.0)
|
||||
a["asset_value"] = (
|
||||
a["gross_purchase_amount"] - a["opening_accumulated_depreciation"] - a["depreciated_amount"]
|
||||
)
|
||||
|
||||
del a["name"]
|
||||
del a["calculate_depreciation"]
|
||||
|
||||
idx = ([i for i, d in enumerate(data) if a[group_by] == d[group_by]] or [None])[0]
|
||||
if idx is None:
|
||||
data.append(a)
|
||||
else:
|
||||
for field in (
|
||||
"gross_purchase_amount",
|
||||
"opening_accumulated_depreciation",
|
||||
"depreciated_amount",
|
||||
"asset_value",
|
||||
):
|
||||
data[idx][field] = data[idx][field] + a[field]
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_purchase_receipt_supplier_map():
|
||||
return frappe._dict(
|
||||
frappe.db.sql(
|
||||
@@ -313,35 +371,35 @@ def get_columns(filters):
|
||||
"fieldtype": "Link",
|
||||
"fieldname": frappe.scrub(filters.get("group_by")),
|
||||
"options": filters.get("group_by"),
|
||||
"width": 120,
|
||||
"width": 216,
|
||||
},
|
||||
{
|
||||
"label": _("Gross Purchase Amount"),
|
||||
"fieldname": "gross_purchase_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100,
|
||||
"width": 250,
|
||||
},
|
||||
{
|
||||
"label": _("Opening Accumulated Depreciation"),
|
||||
"fieldname": "opening_accumulated_depreciation",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 90,
|
||||
"width": 250,
|
||||
},
|
||||
{
|
||||
"label": _("Depreciated Amount"),
|
||||
"fieldname": "depreciated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100,
|
||||
"width": 250,
|
||||
},
|
||||
{
|
||||
"label": _("Asset Value"),
|
||||
"fieldname": "asset_value",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100,
|
||||
"width": 250,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -1635,8 +1635,13 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
self.append("payment_schedule", data)
|
||||
|
||||
allocate_payment_based_on_payment_terms = frappe.db.get_value(
|
||||
"Payment Terms Template", self.payment_terms_template, "allocate_payment_based_on_payment_terms"
|
||||
)
|
||||
|
||||
if not (
|
||||
automatically_fetch_payment_terms
|
||||
and allocate_payment_based_on_payment_terms
|
||||
and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
|
||||
):
|
||||
for d in self.get("payment_schedule"):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import ValidationError, _, msgprint
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.contacts.doctype.address.address import render_address
|
||||
from frappe.utils import cint, cstr, flt, getdate
|
||||
|
||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||
@@ -14,7 +14,8 @@ from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
|
||||
from erpnext.controllers.stock_controller import StockController
|
||||
from erpnext.controllers.subcontracting import Subcontracting
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
from erpnext.stock.utils import get_incoming_rate, get_valuation_method
|
||||
|
||||
|
||||
class QtyMismatchError(ValidationError):
|
||||
@@ -186,7 +187,9 @@ class BuyingController(StockController, Subcontracting):
|
||||
|
||||
for address_field, address_display_field in address_dict.items():
|
||||
if self.get(address_field):
|
||||
self.set(address_display_field, get_address_display(self.get(address_field)))
|
||||
self.set(
|
||||
address_display_field, render_address(self.get(address_field), check_permissions=False)
|
||||
)
|
||||
|
||||
def set_total_in_words(self):
|
||||
from frappe.utils import money_in_words
|
||||
@@ -504,9 +507,20 @@ class BuyingController(StockController, Subcontracting):
|
||||
)
|
||||
|
||||
if self.is_return:
|
||||
outgoing_rate = get_rate_for_return(
|
||||
self.doctype, self.name, d.item_code, self.return_against, item_row=d
|
||||
)
|
||||
if get_valuation_method(d.item_code) == "Moving Average":
|
||||
previous_sle = get_previous_sle(
|
||||
{
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
}
|
||||
)
|
||||
outgoing_rate = flt(previous_sle.get("valuation_rate"))
|
||||
else:
|
||||
outgoing_rate = get_rate_for_return(
|
||||
self.doctype, self.name, d.item_code, self.return_against, item_row=d
|
||||
)
|
||||
|
||||
sle.update({"outgoing_rate": outgoing_rate, "recalculate_rate": 1})
|
||||
if d.from_warehouse:
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _, bold, throw
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime
|
||||
|
||||
from erpnext.accounts.party import render_address
|
||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
|
||||
from erpnext.controllers.stock_controller import StockController
|
||||
@@ -583,7 +583,9 @@ class SellingController(StockController):
|
||||
|
||||
for address_field, address_display_field in address_dict.items():
|
||||
if self.get(address_field):
|
||||
self.set(address_display_field, get_address_display(self.get(address_field)))
|
||||
self.set(
|
||||
address_display_field, render_address(self.get(address_field), check_permissions=False)
|
||||
)
|
||||
|
||||
def validate_for_duplicate_items(self):
|
||||
check_list, chk_dupl_itm = [], []
|
||||
|
||||
@@ -582,13 +582,21 @@ class StockController(AccountsController):
|
||||
d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
|
||||
|
||||
def validate_internal_transfer(self):
|
||||
if (
|
||||
self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt")
|
||||
and self.is_internal_transfer()
|
||||
):
|
||||
self.validate_in_transit_warehouses()
|
||||
self.validate_multi_currency()
|
||||
self.validate_packed_items()
|
||||
if self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt"):
|
||||
if self.is_internal_transfer():
|
||||
self.validate_in_transit_warehouses()
|
||||
self.validate_multi_currency()
|
||||
self.validate_packed_items()
|
||||
else:
|
||||
self.validate_internal_transfer_warehouse()
|
||||
|
||||
def validate_internal_transfer_warehouse(self):
|
||||
for row in self.items:
|
||||
if row.get("target_warehouse"):
|
||||
row.target_warehouse = None
|
||||
|
||||
if row.get("from_warehouse"):
|
||||
row.from_warehouse = None
|
||||
|
||||
def validate_in_transit_warehouses(self):
|
||||
if (
|
||||
@@ -906,8 +914,6 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa
|
||||
|
||||
repost_entry = frappe.new_doc("Repost Item Valuation")
|
||||
repost_entry.based_on = "Item and Warehouse"
|
||||
repost_entry.voucher_type = voucher_type
|
||||
repost_entry.voucher_no = voucher_no
|
||||
|
||||
repost_entry.item_code = sle.item_code
|
||||
repost_entry.warehouse = sle.warehouse
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import Concat_ws, Date
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
@@ -69,53 +70,41 @@ def get_columns():
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
`tabLead`.name,
|
||||
`tabLead`.lead_name,
|
||||
`tabLead`.status,
|
||||
`tabLead`.lead_owner,
|
||||
`tabLead`.territory,
|
||||
`tabLead`.source,
|
||||
`tabLead`.email_id,
|
||||
`tabLead`.mobile_no,
|
||||
`tabLead`.phone,
|
||||
`tabLead`.owner,
|
||||
`tabLead`.company,
|
||||
concat_ws(', ',
|
||||
trim(',' from `tabAddress`.address_line1),
|
||||
trim(',' from tabAddress.address_line2)
|
||||
) AS address,
|
||||
`tabAddress`.state,
|
||||
`tabAddress`.pincode,
|
||||
`tabAddress`.country
|
||||
FROM
|
||||
`tabLead` left join `tabDynamic Link` on (
|
||||
`tabLead`.name = `tabDynamic Link`.link_name and
|
||||
`tabDynamic Link`.parenttype = 'Address')
|
||||
left join `tabAddress` on (
|
||||
`tabAddress`.name=`tabDynamic Link`.parent)
|
||||
WHERE
|
||||
company = %(company)s
|
||||
AND DATE(`tabLead`.creation) BETWEEN %(from_date)s AND %(to_date)s
|
||||
{conditions}
|
||||
ORDER BY
|
||||
`tabLead`.creation asc """.format(
|
||||
conditions=get_conditions(filters)
|
||||
),
|
||||
filters,
|
||||
as_dict=1,
|
||||
lead = frappe.qb.DocType("Lead")
|
||||
address = frappe.qb.DocType("Address")
|
||||
dynamic_link = frappe.qb.DocType("Dynamic Link")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(lead)
|
||||
.left_join(dynamic_link)
|
||||
.on((lead.name == dynamic_link.link_name) & (dynamic_link.parenttype == "Address"))
|
||||
.left_join(address)
|
||||
.on(address.name == dynamic_link.parent)
|
||||
.select(
|
||||
lead.name,
|
||||
lead.lead_name,
|
||||
lead.status,
|
||||
lead.lead_owner,
|
||||
lead.territory,
|
||||
lead.source,
|
||||
lead.email_id,
|
||||
lead.mobile_no,
|
||||
lead.phone,
|
||||
lead.owner,
|
||||
lead.company,
|
||||
(Concat_ws(", ", address.address_line1, address.address_line2)).as_("address"),
|
||||
address.state,
|
||||
address.pincode,
|
||||
address.country,
|
||||
)
|
||||
.where(lead.company == filters.company)
|
||||
.where(Date(lead.creation).between(filters.from_date, filters.to_date))
|
||||
)
|
||||
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = []
|
||||
|
||||
if filters.get("territory"):
|
||||
conditions.append(" and `tabLead`.territory=%(territory)s")
|
||||
query = query.where(lead.territory == filters.get("territory"))
|
||||
|
||||
if filters.get("status"):
|
||||
conditions.append(" and `tabLead`.status=%(status)s")
|
||||
query = query.where(lead.status == filters.get("status"))
|
||||
|
||||
return " ".join(conditions) if conditions else ""
|
||||
return query.run(as_dict=1)
|
||||
|
||||
@@ -188,7 +188,8 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Item Group",
|
||||
"options": "Item Group",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
@@ -234,7 +235,8 @@
|
||||
"fieldname": "brand",
|
||||
"fieldtype": "Link",
|
||||
"label": "Brand",
|
||||
"options": "Brand"
|
||||
"options": "Brand",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@@ -346,7 +348,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2022-09-13 04:05:11.614087",
|
||||
"modified": "2022-09-30 04:01:52.090732",
|
||||
"modified_by": "Administrator",
|
||||
"module": "E-commerce",
|
||||
"name": "Website Item",
|
||||
|
||||
@@ -409,9 +409,6 @@ def on_doctype_update():
|
||||
# since route is a Text column, it needs a length for indexing
|
||||
frappe.db.add_index("Website Item", ["route(500)"])
|
||||
|
||||
frappe.db.add_index("Website Item", ["item_group"])
|
||||
frappe.db.add_index("Website Item", ["brand"])
|
||||
|
||||
|
||||
def check_if_user_is_customer(user=None):
|
||||
from frappe.contacts.doctype.contact.contact import get_contact_name
|
||||
|
||||
@@ -17,7 +17,6 @@ from erpnext.e_commerce.shopping_cart.cart import (
|
||||
request_for_quotation,
|
||||
update_cart,
|
||||
)
|
||||
from erpnext.tests.utils import create_test_contact_and_address
|
||||
|
||||
|
||||
class TestShoppingCart(unittest.TestCase):
|
||||
@@ -28,7 +27,6 @@ class TestShoppingCart(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
frappe.set_user("Administrator")
|
||||
create_test_contact_and_address()
|
||||
self.enable_shopping_cart()
|
||||
if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
|
||||
make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
|
||||
@@ -46,48 +44,57 @@ class TestShoppingCart(unittest.TestCase):
|
||||
frappe.db.sql("delete from `tabTax Rule`")
|
||||
|
||||
def test_get_cart_new_user(self):
|
||||
self.login_as_new_user()
|
||||
|
||||
self.login_as_customer(
|
||||
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
|
||||
)
|
||||
create_address_and_contact(
|
||||
address_title="_Test Address for Customer 2",
|
||||
first_name="_Test Contact for Customer 2",
|
||||
email="test_contact_two_customer@example.com",
|
||||
customer="_Test Customer 2",
|
||||
)
|
||||
# test if lead is created and quotation with new lead is fetched
|
||||
quotation = _get_cart_quotation()
|
||||
customer = frappe.get_doc("Customer", "_Test Customer 2")
|
||||
quotation = _get_cart_quotation(party=customer)
|
||||
self.assertEqual(quotation.quotation_to, "Customer")
|
||||
self.assertEqual(
|
||||
quotation.contact_person,
|
||||
frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com")),
|
||||
frappe.db.get_value("Contact", dict(email_id="test_contact_two_customer@example.com")),
|
||||
)
|
||||
self.assertEqual(quotation.contact_email, frappe.session.user)
|
||||
|
||||
return quotation
|
||||
|
||||
def test_get_cart_customer(self):
|
||||
def validate_quotation():
|
||||
def test_get_cart_customer(self, customer="_Test Customer 2"):
|
||||
def validate_quotation(customer_name):
|
||||
# test if quotation with customer is fetched
|
||||
quotation = _get_cart_quotation()
|
||||
party = frappe.get_doc("Customer", customer_name)
|
||||
quotation = _get_cart_quotation(party=party)
|
||||
self.assertEqual(quotation.quotation_to, "Customer")
|
||||
self.assertEqual(quotation.party_name, "_Test Customer")
|
||||
self.assertEqual(quotation.party_name, customer_name)
|
||||
self.assertEqual(quotation.contact_email, frappe.session.user)
|
||||
return quotation
|
||||
|
||||
self.login_as_customer(
|
||||
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
|
||||
)
|
||||
validate_quotation()
|
||||
|
||||
self.login_as_customer()
|
||||
quotation = validate_quotation()
|
||||
|
||||
quotation = validate_quotation(customer)
|
||||
return quotation
|
||||
|
||||
def test_add_to_cart(self):
|
||||
self.login_as_customer()
|
||||
|
||||
self.login_as_customer(
|
||||
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
|
||||
)
|
||||
create_address_and_contact(
|
||||
address_title="_Test Address for Customer 2",
|
||||
first_name="_Test Contact for Customer 2",
|
||||
email="test_contact_two_customer@example.com",
|
||||
customer="_Test Customer 2",
|
||||
)
|
||||
# clear existing quotations
|
||||
self.clear_existing_quotations()
|
||||
|
||||
# add first item
|
||||
update_cart("_Test Item", 1)
|
||||
|
||||
quotation = self.test_get_cart_customer()
|
||||
quotation = self.test_get_cart_customer("_Test Customer 2")
|
||||
|
||||
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
|
||||
self.assertEqual(quotation.get("items")[0].qty, 1)
|
||||
@@ -95,7 +102,7 @@ class TestShoppingCart(unittest.TestCase):
|
||||
|
||||
# add second item
|
||||
update_cart("_Test Item 2", 1)
|
||||
quotation = self.test_get_cart_customer()
|
||||
quotation = self.test_get_cart_customer("_Test Customer 2")
|
||||
self.assertEqual(quotation.get("items")[1].item_code, "_Test Item 2")
|
||||
self.assertEqual(quotation.get("items")[1].qty, 1)
|
||||
self.assertEqual(quotation.get("items")[1].amount, 20)
|
||||
@@ -108,7 +115,7 @@ class TestShoppingCart(unittest.TestCase):
|
||||
|
||||
# update first item
|
||||
update_cart("_Test Item", 5)
|
||||
quotation = self.test_get_cart_customer()
|
||||
quotation = self.test_get_cart_customer("_Test Customer 2")
|
||||
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
|
||||
self.assertEqual(quotation.get("items")[0].qty, 5)
|
||||
self.assertEqual(quotation.get("items")[0].amount, 50)
|
||||
@@ -121,7 +128,7 @@ class TestShoppingCart(unittest.TestCase):
|
||||
|
||||
# remove first item
|
||||
update_cart("_Test Item", 0)
|
||||
quotation = self.test_get_cart_customer()
|
||||
quotation = self.test_get_cart_customer("_Test Customer 2")
|
||||
|
||||
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item 2")
|
||||
self.assertEqual(quotation.get("items")[0].qty, 1)
|
||||
@@ -129,9 +136,20 @@ class TestShoppingCart(unittest.TestCase):
|
||||
self.assertEqual(quotation.net_total, 20)
|
||||
self.assertEqual(len(quotation.get("items")), 1)
|
||||
|
||||
@unittest.skip("Flaky in CI")
|
||||
def test_tax_rule(self):
|
||||
self.create_tax_rule()
|
||||
self.login_as_customer()
|
||||
|
||||
self.login_as_customer(
|
||||
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
|
||||
)
|
||||
create_address_and_contact(
|
||||
address_title="_Test Address for Customer 2",
|
||||
first_name="_Test Contact for Customer 2",
|
||||
email="test_contact_two_customer@example.com",
|
||||
customer="_Test Customer 2",
|
||||
)
|
||||
|
||||
quotation = self.create_quotation()
|
||||
|
||||
from erpnext.accounts.party import set_taxes
|
||||
@@ -319,7 +337,7 @@ class TestShoppingCart(unittest.TestCase):
|
||||
if frappe.db.exists("User", email):
|
||||
return
|
||||
|
||||
frappe.get_doc(
|
||||
user = frappe.get_doc(
|
||||
{
|
||||
"doctype": "User",
|
||||
"user_type": "Website User",
|
||||
@@ -329,6 +347,40 @@ class TestShoppingCart(unittest.TestCase):
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
user.add_roles("Customer")
|
||||
|
||||
|
||||
def create_address_and_contact(**kwargs):
|
||||
if not frappe.db.get_value("Address", {"address_title": kwargs.get("address_title")}):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_title": kwargs.get("address_title"),
|
||||
"address_type": kwargs.get("address_type") or "Office",
|
||||
"address_line1": kwargs.get("address_line1") or "Station Road",
|
||||
"city": kwargs.get("city") or "_Test City",
|
||||
"state": kwargs.get("state") or "Test State",
|
||||
"country": kwargs.get("country") or "India",
|
||||
"links": [
|
||||
{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
|
||||
],
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.get_value("Contact", {"first_name": kwargs.get("first_name")}):
|
||||
contact = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Contact",
|
||||
"first_name": kwargs.get("first_name"),
|
||||
"links": [
|
||||
{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
|
||||
],
|
||||
}
|
||||
)
|
||||
contact.add_email(kwargs.get("email") or "test_contact_customer@example.com", is_primary=True)
|
||||
contact.add_phone(kwargs.get("phone") or "+91 0000000000", is_primary_phone=True)
|
||||
contact.insert()
|
||||
|
||||
|
||||
test_dependencies = [
|
||||
"Sales Taxes and Charges Template",
|
||||
|
||||
@@ -108,8 +108,8 @@ class EmployeeAdvance(Document):
|
||||
EmployeeAdvanceOverPayment,
|
||||
)
|
||||
|
||||
if flt(return_amount) > self.paid_amount - self.claimed_amount:
|
||||
frappe.throw(_("Return amount cannot be greater unclaimed amount"))
|
||||
if flt(return_amount) > 0 and flt(return_amount) > (self.paid_amount - self.claimed_amount):
|
||||
frappe.throw(_("Return amount cannot be greater than unclaimed amount"))
|
||||
|
||||
self.db_set("paid_amount", paid_amount)
|
||||
self.db_set("return_amount", return_amount)
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
frappe.ui.form.on('Loan Repayment', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
// },
|
||||
|
||||
setup: function(frm) {
|
||||
frm.add_fetch("against_loan", "repay_from_salary", "repay_from_salary");
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
frm.set_query('against_loan', function() {
|
||||
return {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"posting_date",
|
||||
"clearance_date",
|
||||
"rate_of_interest",
|
||||
"payroll_payable_account",
|
||||
"is_term_loan",
|
||||
"repay_from_salary",
|
||||
"payment_details_section",
|
||||
@@ -41,6 +40,7 @@
|
||||
"amended_from",
|
||||
"accounting_details_section",
|
||||
"payment_account",
|
||||
"payroll_payable_account",
|
||||
"penalty_income_account",
|
||||
"column_break_36",
|
||||
"loan_account"
|
||||
@@ -262,7 +262,6 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "against_loan.repay_from_salary",
|
||||
"fieldname": "repay_from_salary",
|
||||
"fieldtype": "Check",
|
||||
"label": "Repay From Salary"
|
||||
@@ -280,6 +279,7 @@
|
||||
"label": "Accounting Details"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.repay_from_salary",
|
||||
"fetch_from": "against_loan.payment_account",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "payment_account",
|
||||
@@ -311,11 +311,10 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-08-04 17:13:51.964203",
|
||||
"modified": "2023-09-18 16:50:32.897005",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan Repayment",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -351,6 +350,5 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,11 @@ class LoanRepayment(AccountsController):
|
||||
if amounts.get("due_date"):
|
||||
self.due_date = amounts.get("due_date")
|
||||
|
||||
if self.repay_from_salary and not self.payroll_payable_account:
|
||||
frappe.throw(_("Please set Payroll Payable Account in Loan Repayment"))
|
||||
elif not self.repay_from_salary and self.payroll_payable_account:
|
||||
self.repay_from_salary = 1
|
||||
|
||||
def check_future_entries(self):
|
||||
future_repayment_date = frappe.db.get_value(
|
||||
"Loan Repayment",
|
||||
@@ -250,6 +255,9 @@ class LoanRepayment(AccountsController):
|
||||
)
|
||||
|
||||
def check_future_accruals(self):
|
||||
if self.is_term_loan:
|
||||
return
|
||||
|
||||
future_accrual_date = frappe.db.get_value(
|
||||
"Loan Interest Accrual",
|
||||
{"posting_date": (">", self.posting_date), "docstatus": 1, "loan": self.against_loan},
|
||||
|
||||
@@ -79,6 +79,7 @@ class BOMUpdateLog(Document):
|
||||
else:
|
||||
frappe.enqueue(
|
||||
method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
|
||||
queue="long",
|
||||
update_doc=self,
|
||||
now=frappe.flags.in_test,
|
||||
)
|
||||
|
||||
@@ -157,12 +157,21 @@ def get_next_higher_level_boms(
|
||||
def get_leaf_boms() -> List[str]:
|
||||
"Get BOMs that have no dependencies."
|
||||
|
||||
return frappe.db.sql_list(
|
||||
"""select name from `tabBOM` bom
|
||||
where docstatus=1 and is_active=1
|
||||
and not exists(select bom_no from `tabBOM Item`
|
||||
where parent=bom.name and ifnull(bom_no, '')!='')"""
|
||||
)
|
||||
bom = frappe.qb.DocType("BOM")
|
||||
bom_item = frappe.qb.DocType("BOM Item")
|
||||
|
||||
boms = (
|
||||
frappe.qb.from_(bom)
|
||||
.left_join(bom_item)
|
||||
.on((bom.name == bom_item.parent) & (bom_item.bom_no != ""))
|
||||
.select(bom.name)
|
||||
.where((bom.docstatus == 1) & (bom.is_active == 1) & (bom_item.bom_no.isnull()))
|
||||
.distinct()
|
||||
).run(as_list=True)
|
||||
|
||||
boms = [bom[0] for bom in boms]
|
||||
|
||||
return boms
|
||||
|
||||
|
||||
def _generate_dependence_map() -> defaultdict:
|
||||
|
||||
@@ -675,10 +675,9 @@ class ProductionPlan(Document):
|
||||
material_request.flags.ignore_permissions = 1
|
||||
material_request.run_method("set_missing_values")
|
||||
|
||||
material_request.save()
|
||||
if self.get("submit_material_request"):
|
||||
material_request.submit()
|
||||
else:
|
||||
material_request.save()
|
||||
|
||||
frappe.flags.mute_messages = False
|
||||
|
||||
|
||||
@@ -377,3 +377,5 @@ execute:frappe.db.set_value("Naming Series", "Naming Series", {"select_doc_for_s
|
||||
erpnext.patches.v13_0.update_schedule_type_in_loans
|
||||
erpnext.patches.v13_0.update_asset_value_for_manual_depr_entries
|
||||
erpnext.patches.v13_0.update_docs_link
|
||||
erpnext.patches.v13_0.correct_asset_value_if_je_with_workflow
|
||||
execute:frappe.db.set_value("Accounts Settings", "Accounts Settings", "service_provider", "frankfurter.app")
|
||||
|
||||
@@ -3,23 +3,24 @@ import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("stock", "doctype", "quality_inspection_parameter")
|
||||
params = set()
|
||||
|
||||
# get all distinct parameters from QI readigs table
|
||||
reading_params = frappe.db.get_all(
|
||||
"Quality Inspection Reading", fields=["distinct specification"]
|
||||
)
|
||||
reading_params = [d.specification for d in reading_params]
|
||||
# get all parameters from QI readings table
|
||||
for (p,) in frappe.db.get_all(
|
||||
"Quality Inspection Reading", fields=["specification"], as_list=True
|
||||
):
|
||||
params.add(p.strip())
|
||||
|
||||
# get all distinct parameters from QI Template as some may be unused in QI
|
||||
template_params = frappe.db.get_all(
|
||||
"Item Quality Inspection Parameter", fields=["distinct specification"]
|
||||
)
|
||||
template_params = [d.specification for d in template_params]
|
||||
# get all parameters from QI Template as some may be unused in QI
|
||||
for (p,) in frappe.db.get_all(
|
||||
"Item Quality Inspection Parameter", fields=["specification"], as_list=True
|
||||
):
|
||||
params.add(p.strip())
|
||||
|
||||
params = list(set(reading_params + template_params))
|
||||
# because db primary keys are case insensitive, so duplicates will cause an exception
|
||||
params = set({x.casefold(): x for x in params}.values())
|
||||
|
||||
for parameter in params:
|
||||
if not frappe.db.exists("Quality Inspection Parameter", parameter):
|
||||
frappe.get_doc(
|
||||
{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
|
||||
).insert(ignore_permissions=True)
|
||||
frappe.get_doc(
|
||||
{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
119
erpnext/patches/v13_0/correct_asset_value_if_je_with_workflow.py
Normal file
119
erpnext/patches/v13_0/correct_asset_value_if_je_with_workflow.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import frappe
|
||||
from frappe.model.workflow import get_workflow_name
|
||||
from frappe.query_builder.functions import IfNull, Sum
|
||||
|
||||
|
||||
def execute():
|
||||
active_je_workflow = get_workflow_name("Journal Entry")
|
||||
if not active_je_workflow:
|
||||
return
|
||||
|
||||
correct_value_for_assets_with_manual_depr_entries()
|
||||
|
||||
finance_books = frappe.db.get_all("Finance Book", pluck="name")
|
||||
|
||||
if finance_books:
|
||||
for fb_name in finance_books:
|
||||
correct_value_for_assets_with_auto_depr(fb_name)
|
||||
|
||||
correct_value_for_assets_with_auto_depr()
|
||||
|
||||
|
||||
def correct_value_for_assets_with_manual_depr_entries():
|
||||
asset = frappe.qb.DocType("Asset")
|
||||
gle = frappe.qb.DocType("GL Entry")
|
||||
aca = frappe.qb.DocType("Asset Category Account")
|
||||
company = frappe.qb.DocType("Company")
|
||||
|
||||
asset_details_and_depr_amount_map = (
|
||||
frappe.qb.from_(gle)
|
||||
.join(asset)
|
||||
.on(gle.against_voucher == asset.name)
|
||||
.join(aca)
|
||||
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
|
||||
.join(company)
|
||||
.on(company.name == asset.company)
|
||||
.select(
|
||||
asset.name.as_("asset_name"),
|
||||
asset.gross_purchase_amount.as_("gross_purchase_amount"),
|
||||
asset.opening_accumulated_depreciation.as_("opening_accumulated_depreciation"),
|
||||
Sum(gle.debit).as_("depr_amount"),
|
||||
)
|
||||
.where(
|
||||
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
||||
)
|
||||
.where(gle.debit != 0)
|
||||
.where(gle.is_cancelled == 0)
|
||||
.where(asset.docstatus == 1)
|
||||
.where(asset.calculate_depreciation == 0)
|
||||
.groupby(asset.name)
|
||||
)
|
||||
|
||||
frappe.qb.update(asset).join(asset_details_and_depr_amount_map).on(
|
||||
asset_details_and_depr_amount_map.asset_name == asset.name
|
||||
).set(
|
||||
asset.value_after_depreciation,
|
||||
asset_details_and_depr_amount_map.gross_purchase_amount
|
||||
- asset_details_and_depr_amount_map.opening_accumulated_depreciation
|
||||
- asset_details_and_depr_amount_map.depr_amount,
|
||||
).run()
|
||||
|
||||
|
||||
def correct_value_for_assets_with_auto_depr(fb_name=None):
|
||||
asset = frappe.qb.DocType("Asset")
|
||||
gle = frappe.qb.DocType("GL Entry")
|
||||
aca = frappe.qb.DocType("Asset Category Account")
|
||||
company = frappe.qb.DocType("Company")
|
||||
afb = frappe.qb.DocType("Asset Finance Book")
|
||||
|
||||
asset_details_and_depr_amount_map = (
|
||||
frappe.qb.from_(gle)
|
||||
.join(asset)
|
||||
.on(gle.against_voucher == asset.name)
|
||||
.join(aca)
|
||||
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
|
||||
.join(company)
|
||||
.on(company.name == asset.company)
|
||||
.select(
|
||||
asset.name.as_("asset_name"),
|
||||
asset.gross_purchase_amount.as_("gross_purchase_amount"),
|
||||
asset.opening_accumulated_depreciation.as_("opening_accumulated_depreciation"),
|
||||
Sum(gle.debit).as_("depr_amount"),
|
||||
)
|
||||
.where(
|
||||
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
||||
)
|
||||
.where(gle.debit != 0)
|
||||
.where(gle.is_cancelled == 0)
|
||||
.where(asset.docstatus == 1)
|
||||
.where(asset.calculate_depreciation == 1)
|
||||
.groupby(asset.name)
|
||||
)
|
||||
|
||||
if fb_name:
|
||||
asset_details_and_depr_amount_map = asset_details_and_depr_amount_map.where(
|
||||
gle.finance_book == fb_name
|
||||
)
|
||||
else:
|
||||
asset_details_and_depr_amount_map = asset_details_and_depr_amount_map.where(
|
||||
(gle.finance_book.isin([""])) | (gle.finance_book.isnull())
|
||||
)
|
||||
|
||||
query = (
|
||||
frappe.qb.update(afb)
|
||||
.join(asset_details_and_depr_amount_map)
|
||||
.on(asset_details_and_depr_amount_map.asset_name == afb.parent)
|
||||
.set(
|
||||
afb.value_after_depreciation,
|
||||
asset_details_and_depr_amount_map.gross_purchase_amount
|
||||
- asset_details_and_depr_amount_map.opening_accumulated_depreciation
|
||||
- asset_details_and_depr_amount_map.depr_amount,
|
||||
)
|
||||
)
|
||||
|
||||
if fb_name:
|
||||
query = query.where(afb.finance_book == fb_name)
|
||||
else:
|
||||
query = query.where((afb.finance_book.isin([""])) | (afb.finance_book.isnull()))
|
||||
|
||||
query.run()
|
||||
@@ -125,6 +125,7 @@ def execute():
|
||||
loan_type_doc.company = loan.company
|
||||
loan_type_doc.mode_of_payment = loan.mode_of_payment
|
||||
loan_type_doc.payment_account = loan.payment_account
|
||||
loan_type_doc.disbursement_account = loan.payment_account
|
||||
loan_type_doc.loan_account = loan.loan_account
|
||||
loan_type_doc.interest_income_account = loan.interest_income_account
|
||||
loan_type_doc.penalty_income_account = penalty_account
|
||||
|
||||
@@ -46,6 +46,7 @@ from erpnext.payroll.doctype.payroll_period.payroll_period import (
|
||||
get_payroll_period,
|
||||
get_period_factor,
|
||||
)
|
||||
from erpnext.payroll.utils import prepare_error_msg, sanitize_expression
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
|
||||
@@ -726,32 +727,53 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
return data, default_data
|
||||
|
||||
def eval_condition_and_formula(self, d, data):
|
||||
def eval_condition_and_formula(self, struct_row, data):
|
||||
try:
|
||||
condition = d.condition.strip().replace("\n", " ") if d.condition else None
|
||||
condition = sanitize_expression(struct_row.condition)
|
||||
if condition:
|
||||
if not frappe.safe_eval(condition, self.whitelisted_globals, data):
|
||||
return None
|
||||
amount = d.amount
|
||||
if d.amount_based_on_formula:
|
||||
formula = d.formula.strip().replace("\n", " ") if d.formula else None
|
||||
amount = struct_row.amount
|
||||
if struct_row.amount_based_on_formula:
|
||||
formula = sanitize_expression(struct_row.formula)
|
||||
if formula:
|
||||
amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount"))
|
||||
amount = flt(
|
||||
frappe.safe_eval(formula, self.whitelisted_globals, data), struct_row.precision("amount")
|
||||
)
|
||||
if amount:
|
||||
data[d.abbr] = amount
|
||||
data[struct_row.abbr] = amount
|
||||
|
||||
return amount
|
||||
|
||||
except NameError as err:
|
||||
frappe.throw(
|
||||
_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
||||
title=_("Name error"),
|
||||
except NameError as ne:
|
||||
message = prepare_error_msg(
|
||||
row=struct_row,
|
||||
error=ne,
|
||||
expression=formula or condition,
|
||||
description=_("This error can be due to missing or deleted field."),
|
||||
)
|
||||
except SyntaxError as err:
|
||||
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
|
||||
|
||||
frappe.throw(message, title=_("Name error"))
|
||||
|
||||
except SyntaxError as se:
|
||||
message = prepare_error_msg(
|
||||
row=struct_row,
|
||||
error=se,
|
||||
expression=formula or condition,
|
||||
description=_("Please check the syntax of your formula."),
|
||||
)
|
||||
|
||||
frappe.throw(message, title=_("Syntax error"))
|
||||
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
||||
raise
|
||||
message = prepare_error_msg(
|
||||
row=struct_row,
|
||||
error=e,
|
||||
expression=formula or condition,
|
||||
description=_("This error can be due to invalid formula or condition."),
|
||||
)
|
||||
|
||||
frappe.throw(message, title=_("Error in formula or condition"))
|
||||
|
||||
def add_employee_benefits(self, payroll_period):
|
||||
for struct_row in self._salary_structure_doc.get("earnings"):
|
||||
|
||||
66
erpnext/payroll/utils.py
Normal file
66
erpnext/payroll/utils.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import get_link_to_form
|
||||
|
||||
|
||||
def sanitize_expression(string: Optional[str] = None) -> Optional[str]:
|
||||
"""
|
||||
Sanitizes an expression string by removing leading/trailing spaces, newlines, and line boundaries.
|
||||
|
||||
Args:
|
||||
string (Optional[str]): The input string to be sanitized (default: None).
|
||||
|
||||
Returns:
|
||||
Optional[str]: The sanitized string or None if the input string is empty or None.
|
||||
|
||||
Example:
|
||||
expression = "\r\n gross_pay > 10000\n "
|
||||
sanitized_expr = sanitize_expression(expression)
|
||||
"""
|
||||
if not string:
|
||||
return None
|
||||
|
||||
parts = string.strip().splitlines()
|
||||
string = " ".join(parts)
|
||||
|
||||
return string
|
||||
|
||||
|
||||
def prepare_error_msg(*, row: dict, error: str, expression: str, description: str) -> str:
|
||||
"""
|
||||
Prepares an error message string with formatted information about the error.
|
||||
|
||||
Args:
|
||||
row (dict): A dictionary representing the row data.
|
||||
error (str): The error message.
|
||||
expression (str): The expression that caused the error.
|
||||
description (str): Additional description or hint for the error (optional).
|
||||
|
||||
Returns:
|
||||
str: The formatted error message string.
|
||||
|
||||
Example:
|
||||
row = {
|
||||
"parenttype": "Salary Structure",
|
||||
"parent": "Salary Structure-00001",
|
||||
"parentfield": "earnings",
|
||||
"idx": 1
|
||||
}
|
||||
error = "SyntaxError: invalid syntax"
|
||||
expression = " 200 if (gross_pay>10000 and month!= 'Feb')) else 0 "
|
||||
description = "Check the syntax of the expression."
|
||||
error_msg = prepare_error_msg(row=row, error=error, expression=expression, description=description)
|
||||
"""
|
||||
msg = _("Error in {0} while evaluating the {1} {2} at row {3}").format(
|
||||
row.parentfield.title(), row.parenttype, get_link_to_form(row.parenttype, row.parent), row.idx
|
||||
)
|
||||
msg += "<br><br>{0}: {1}<br><br>{2}: {3}".format(
|
||||
frappe.bold(_("Expression:")), expression, frappe.bold(_("Error:")), error
|
||||
)
|
||||
|
||||
if description:
|
||||
msg += "<br><br>{0}: {1}".format(frappe.bold(_("Hint:")), description)
|
||||
|
||||
return msg
|
||||
@@ -1068,6 +1068,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
this.frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
|
||||
},
|
||||
|
||||
apply_discount_on_item: function(doc, cdt, cdn, field) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
if(!item.price_list_rate) {
|
||||
item[field] = 0.0;
|
||||
} else {
|
||||
this.price_list_rate(doc, cdt, cdn);
|
||||
}
|
||||
this.set_gross_profit(item);
|
||||
},
|
||||
|
||||
shipping_rule: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.shipping_rule) {
|
||||
@@ -1720,6 +1730,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
() => {
|
||||
if(args.items.length) {
|
||||
me._set_values_for_item_list(r.message.children);
|
||||
$.each(r.message.children || [], function(i, d) {
|
||||
me.apply_discount_on_item(d, d.doctype, d.name, 'discount_percentage');
|
||||
});
|
||||
}
|
||||
},
|
||||
() => { me.in_apply_price_list = false; }
|
||||
|
||||
@@ -6,8 +6,10 @@ erpnext.financial_statements = {
|
||||
if (data && column.fieldname=="account") {
|
||||
value = data.account_name || value;
|
||||
|
||||
column.link_onclick =
|
||||
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
|
||||
if (data.account) {
|
||||
column.link_onclick =
|
||||
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
|
||||
}
|
||||
column.is_tree = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -566,7 +566,6 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
fields.splice(3, 0, {
|
||||
fieldtype: 'Float',
|
||||
fieldname: "conversion_factor",
|
||||
in_list_view: 1,
|
||||
label: __("Conversion Factor"),
|
||||
precision: get_precision('conversion_factor')
|
||||
})
|
||||
@@ -574,6 +573,7 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
|
||||
new frappe.ui.Dialog({
|
||||
title: __("Update Items"),
|
||||
size: "extra-large",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "trans_items",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "format:{####}",
|
||||
"creation": "2019-05-26 15:03:43.996455",
|
||||
"doctype": "DocType",
|
||||
@@ -12,7 +13,6 @@
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fetch_from": "goal.objective",
|
||||
"fieldname": "objective",
|
||||
"fieldtype": "Text",
|
||||
"in_list_view": 1,
|
||||
@@ -38,14 +38,17 @@
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-05-26 16:12:54.832058",
|
||||
"links": [],
|
||||
"modified": "2023-07-28 18:10:23.351246",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Quality Management",
|
||||
"name": "Quality Goal Objective",
|
||||
"naming_rule": "Expression",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1378,7 +1378,7 @@ class GSPConnector:
|
||||
|
||||
def set_einvoice_data(self, res):
|
||||
enc_signed_invoice = res.get("SignedInvoice")
|
||||
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)["data"]
|
||||
dec_signed_invoice = jwt.decode(enc_signed_invoice, options={"verify_signature": False})["data"]
|
||||
|
||||
self.invoice.irn = res.get("Irn")
|
||||
self.invoice.ewaybill = res.get("EwbNo")
|
||||
|
||||
@@ -28,7 +28,7 @@ def execute(filters=None):
|
||||
"gst_category",
|
||||
"export_type",
|
||||
"ecommerce_gstin",
|
||||
"`tabPurchase Invoice Item`.gst_hsn_code",
|
||||
"gst_hsn_code",
|
||||
"bill_no",
|
||||
"bill_date",
|
||||
],
|
||||
|
||||
@@ -39,7 +39,7 @@ def execute(filters=None):
|
||||
"gst_category",
|
||||
"export_type",
|
||||
"ecommerce_gstin",
|
||||
"`tabSales Invoice Item`.gst_hsn_code",
|
||||
"gst_hsn_code",
|
||||
]
|
||||
|
||||
additional_conditions = get_conditions(filters, additional_query_columns)
|
||||
|
||||
@@ -96,18 +96,26 @@ class SalesOrder(SellingController):
|
||||
and customer = %s",
|
||||
(self.po_no, self.name, self.customer),
|
||||
)
|
||||
if (
|
||||
so
|
||||
and so[0][0]
|
||||
and not cint(
|
||||
if so and so[0][0]:
|
||||
if cint(
|
||||
frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders")
|
||||
)
|
||||
):
|
||||
frappe.msgprint(
|
||||
_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(
|
||||
so[0][0], self.po_no
|
||||
):
|
||||
frappe.msgprint(
|
||||
_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(
|
||||
frappe.bold(so[0][0]), frappe.bold(self.po_no)
|
||||
)
|
||||
)
|
||||
else:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}"
|
||||
).format(
|
||||
frappe.bold(so[0][0]),
|
||||
frappe.bold(self.po_no),
|
||||
frappe.bold(_("'Allow Multiple Sales Orders Against a Customer's Purchase Order'")),
|
||||
get_link_to_form("Selling Settings", "Selling Settings"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def validate_for_items(self):
|
||||
for d in self.get("items"):
|
||||
|
||||
@@ -1741,7 +1741,7 @@ def make_sales_order(**args):
|
||||
so.company = args.company or "_Test Company"
|
||||
so.customer = args.customer or "_Test Customer"
|
||||
so.currency = args.currency or "INR"
|
||||
so.po_no = args.po_no or "12345"
|
||||
so.po_no = args.po_no or ""
|
||||
if args.selling_price_list:
|
||||
so.selling_price_list = args.selling_price_list
|
||||
|
||||
|
||||
@@ -145,16 +145,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount');
|
||||
},
|
||||
|
||||
apply_discount_on_item: function(doc, cdt, cdn, field) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
if(!item.price_list_rate) {
|
||||
item[field] = 0.0;
|
||||
} else {
|
||||
this.price_list_rate(doc, cdt, cdn);
|
||||
}
|
||||
this.set_gross_profit(item);
|
||||
},
|
||||
|
||||
commission_rate: function() {
|
||||
this.calculate_commission();
|
||||
},
|
||||
|
||||
@@ -75,7 +75,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling")
|
||||
self.assertFalse(exchange_rate == 60)
|
||||
self.assertEqual(flt(exchange_rate, 3), 66.999)
|
||||
self.assertEqual(flt(exchange_rate, 3), 66.894)
|
||||
|
||||
def test_exchange_rate_strict(self):
|
||||
# strict currency settings
|
||||
@@ -87,7 +87,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
|
||||
self.assertEqual(flt(exchange_rate, 3), 67.235)
|
||||
self.assertEqual(flt(exchange_rate, 3), 67.79)
|
||||
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
|
||||
self.assertEqual(exchange_rate, 62.9)
|
||||
@@ -95,7 +95,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
# Exchange rate as on 15th Dec, 2015
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying")
|
||||
self.assertEqual(flt(exchange_rate, 3), 66.999)
|
||||
self.assertEqual(flt(exchange_rate, 3), 66.894)
|
||||
|
||||
def test_exchange_rate_strict_switched(self):
|
||||
# Start with allow_stale is True
|
||||
@@ -108,4 +108,4 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
# Will fetch from fixer.io
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
|
||||
self.assertEqual(flt(exchange_rate, 3), 67.235)
|
||||
self.assertEqual(flt(exchange_rate, 3), 67.79)
|
||||
|
||||
@@ -33,6 +33,7 @@ def after_install():
|
||||
add_standard_navbar_items()
|
||||
add_app_name()
|
||||
add_non_standard_user_types()
|
||||
update_roles()
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
@@ -237,6 +238,12 @@ def create_custom_role(data):
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def update_roles():
|
||||
website_user_roles = ("Customer", "Supplier")
|
||||
for role in website_user_roles:
|
||||
frappe.db.set_value("Role", role, "desk_access", 0)
|
||||
|
||||
|
||||
def create_user_type(user_type, data):
|
||||
if frappe.db.exists("User Type", user_type):
|
||||
doc = frappe.get_cached_doc("User Type", user_type)
|
||||
|
||||
@@ -113,13 +113,30 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
|
||||
if not value:
|
||||
import requests
|
||||
|
||||
api_url = "https://api.exchangerate.host/convert"
|
||||
response = requests.get(
|
||||
api_url, params={"date": transaction_date, "from": from_currency, "to": to_currency}
|
||||
)
|
||||
if currency_settings.service_provider == "exchangerate.host":
|
||||
api_url = "https://api.exchangerate.host/convert"
|
||||
response = requests.get(
|
||||
api_url,
|
||||
params={
|
||||
"access_key": currency_settings.access_key,
|
||||
"transaction_date": transaction_date,
|
||||
"amount": 1,
|
||||
"from": from_currency,
|
||||
"to": to_currency,
|
||||
},
|
||||
)
|
||||
# exchangerate.host return 200 for all requests. Can't rely on it to raise exception
|
||||
value = response.json()["result"]
|
||||
if not response.json()["success"]:
|
||||
raise frappe.ValidationError
|
||||
|
||||
else:
|
||||
api_url = f"https://api.frankfurter.app/{transaction_date}"
|
||||
response = requests.get(api_url, params={"from": from_currency, "to": to_currency})
|
||||
value = response.json()["rates"][to_currency]
|
||||
|
||||
# expire in 6 hours
|
||||
response.raise_for_status()
|
||||
value = response.json()["result"]
|
||||
cache.setex(name=key, time=21600, value=flt(value))
|
||||
return flt(value)
|
||||
except Exception:
|
||||
|
||||
@@ -1268,6 +1268,7 @@
|
||||
"depends_on": "eval: doc.is_internal_customer",
|
||||
"fieldname": "set_target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Set Target Warehouse",
|
||||
"no_copy": 1,
|
||||
@@ -1335,7 +1336,7 @@
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-09-16 17:46:17.701904",
|
||||
"modified": "2023-09-04 14:15:28.363184",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
@@ -1405,4 +1406,4 @@
|
||||
"title_field": "title",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,7 +697,7 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
|
||||
def test_dn_billing_status_case1(self):
|
||||
# SO -> DN -> SI
|
||||
so = make_sales_order()
|
||||
so = make_sales_order(po_no="12345")
|
||||
dn = create_dn_against_so(so.name, delivered_qty=2)
|
||||
|
||||
self.assertEqual(dn.status, "To Bill")
|
||||
@@ -724,7 +724,7 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
make_sales_invoice,
|
||||
)
|
||||
|
||||
so = make_sales_order()
|
||||
so = make_sales_order(po_no="12345")
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.get("items")[0].qty = 5
|
||||
@@ -768,7 +768,7 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
|
||||
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
||||
|
||||
so = make_sales_order()
|
||||
so = make_sales_order(po_no="12345")
|
||||
|
||||
dn1 = make_delivery_note(so.name)
|
||||
dn1.get("items")[0].qty = 2
|
||||
@@ -814,7 +814,7 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
|
||||
|
||||
so = make_sales_order()
|
||||
so = make_sales_order(po_no="12345")
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.submit()
|
||||
@@ -1180,6 +1180,25 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
|
||||
self.assertTrue(return_dn.docstatus == 1)
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
|
||||
|
||||
def test_non_internal_transfer_delivery_note(self):
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
dn = create_delivery_note(do_not_submit=True)
|
||||
warehouse = create_warehouse("Internal Transfer Warehouse", company=dn.company)
|
||||
dn.items[0].db_set("target_warehouse", warehouse)
|
||||
|
||||
dn.reload()
|
||||
|
||||
self.assertEqual(dn.items[0].target_warehouse, warehouse)
|
||||
|
||||
dn.save()
|
||||
dn.reload()
|
||||
self.assertFalse(dn.items[0].target_warehouse)
|
||||
|
||||
|
||||
def create_delivery_note(**args):
|
||||
dn = frappe.new_doc("Delivery Note")
|
||||
|
||||
@@ -213,7 +213,8 @@
|
||||
"default": "0",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disabled"
|
||||
"label": "Disabled",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -956,7 +957,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2022-09-13 04:08:17.431731",
|
||||
"modified": "2023-07-14 17:18:18.658942",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
||||
@@ -742,6 +742,8 @@ def create_item(
|
||||
opening_stock=0,
|
||||
is_fixed_asset=0,
|
||||
asset_category=None,
|
||||
buying_cost_center=None,
|
||||
selling_cost_center=None,
|
||||
company="_Test Company",
|
||||
):
|
||||
if not frappe.db.exists("Item", item_code):
|
||||
@@ -759,7 +761,15 @@ def create_item(
|
||||
item.is_purchase_item = is_purchase_item
|
||||
item.is_customer_provided_item = is_customer_provided_item
|
||||
item.customer = customer or ""
|
||||
item.append("item_defaults", {"default_warehouse": warehouse, "company": company})
|
||||
item.append(
|
||||
"item_defaults",
|
||||
{
|
||||
"default_warehouse": warehouse,
|
||||
"company": company,
|
||||
"selling_cost_center": selling_cost_center,
|
||||
"buying_cost_center": buying_cost_center,
|
||||
},
|
||||
)
|
||||
item.save()
|
||||
else:
|
||||
item = frappe.get_doc("Item", item_code)
|
||||
|
||||
@@ -1,370 +1,90 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2015-05-19 05:12:30.344797",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
"actions": [],
|
||||
"creation": "2015-05-19 05:12:30.344797",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"variant_of",
|
||||
"attribute",
|
||||
"column_break_2",
|
||||
"attribute_value",
|
||||
"numeric_values",
|
||||
"section_break_4",
|
||||
"from_range",
|
||||
"increment",
|
||||
"column_break_8",
|
||||
"to_range"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "variant_of",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Variant Of",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Item",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "variant_of",
|
||||
"fieldtype": "Link",
|
||||
"label": "Variant Of",
|
||||
"options": "Item",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "attribute",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Attribute",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Item Attribute",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "attribute",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Attribute",
|
||||
"options": "Item Attribute",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "attribute_value",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Attribute Value",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "attribute_value",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Attribute Value"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "has_variants",
|
||||
"fieldname": "numeric_values",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Numeric Values",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"depends_on": "has_variants",
|
||||
"fieldname": "numeric_values",
|
||||
"fieldtype": "Check",
|
||||
"label": "Numeric Values"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "numeric_values",
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "numeric_values",
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "from_range",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "From Range",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "from_range",
|
||||
"fieldtype": "Float",
|
||||
"label": "From Range"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "increment",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Increment",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "increment",
|
||||
"fieldtype": "Float",
|
||||
"label": "Increment"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "to_range",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "To Range",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "to_range",
|
||||
"fieldtype": "Float",
|
||||
"label": "To Range"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "",
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-03 15:36:59.129006",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Variant Attribute",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-07-14 17:15:19.112119",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Variant Attribute",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -1069,88 +1069,6 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
pr1.reload()
|
||||
pr1.cancel()
|
||||
|
||||
def test_stock_transfer_from_purchase_receipt(self):
|
||||
pr1 = make_purchase_receipt(
|
||||
warehouse="Work In Progress - TCP1", company="_Test Company with perpetual inventory"
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
|
||||
)
|
||||
|
||||
pr.supplier_warehouse = ""
|
||||
pr.items[0].from_warehouse = "Work In Progress - TCP1"
|
||||
|
||||
pr.submit()
|
||||
|
||||
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
||||
sl_entries = get_sl_entries("Purchase Receipt", pr.name)
|
||||
|
||||
self.assertFalse(gl_entries)
|
||||
|
||||
expected_sle = {"Work In Progress - TCP1": -5, "Stores - TCP1": 5}
|
||||
|
||||
for sle in sl_entries:
|
||||
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
|
||||
|
||||
pr.cancel()
|
||||
pr1.cancel()
|
||||
|
||||
def test_stock_transfer_from_purchase_receipt_with_valuation(self):
|
||||
create_warehouse(
|
||||
"_Test Warehouse for Valuation",
|
||||
company="_Test Company with perpetual inventory",
|
||||
properties={"account": "_Test Account Stock In Hand - TCP1"},
|
||||
)
|
||||
|
||||
pr1 = make_purchase_receipt(
|
||||
warehouse="_Test Warehouse for Valuation - TCP1",
|
||||
company="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
|
||||
)
|
||||
|
||||
pr.items[0].from_warehouse = "_Test Warehouse for Valuation - TCP1"
|
||||
pr.supplier_warehouse = ""
|
||||
|
||||
pr.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Shipping Charges - TCP1",
|
||||
"category": "Valuation and Total",
|
||||
"cost_center": "Main - TCP1",
|
||||
"description": "Test",
|
||||
"rate": 9,
|
||||
},
|
||||
)
|
||||
|
||||
pr.submit()
|
||||
|
||||
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
||||
sl_entries = get_sl_entries("Purchase Receipt", pr.name)
|
||||
|
||||
expected_gle = [
|
||||
["Stock In Hand - TCP1", 272.5, 0.0],
|
||||
["_Test Account Stock In Hand - TCP1", 0.0, 250.0],
|
||||
["_Test Account Shipping Charges - TCP1", 0.0, 22.5],
|
||||
]
|
||||
|
||||
expected_sle = {"_Test Warehouse for Valuation - TCP1": -5, "Stores - TCP1": 5}
|
||||
|
||||
for sle in sl_entries:
|
||||
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(gle.account, expected_gle[i][0])
|
||||
self.assertEqual(gle.debit, expected_gle[i][1])
|
||||
self.assertEqual(gle.credit, expected_gle[i][2])
|
||||
|
||||
pr.cancel()
|
||||
pr1.cancel()
|
||||
|
||||
def test_subcontracted_pr_for_multi_transfer_batches(self):
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import (
|
||||
make_purchase_receipt,
|
||||
@@ -1944,6 +1862,32 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
ste5.reload()
|
||||
self.assertEqual(ste5.items[0].valuation_rate, 275.00)
|
||||
|
||||
ste6 = make_stock_entry(
|
||||
purpose="Material Transfer",
|
||||
posting_date=add_days(today(), -3),
|
||||
source=warehouse1,
|
||||
target=warehouse,
|
||||
item_code=item_code,
|
||||
qty=20,
|
||||
company=pr.company,
|
||||
)
|
||||
|
||||
ste6.reload()
|
||||
self.assertEqual(ste6.items[0].valuation_rate, 275.00)
|
||||
|
||||
ste7 = make_stock_entry(
|
||||
purpose="Material Transfer",
|
||||
posting_date=add_days(today(), -3),
|
||||
source=warehouse,
|
||||
target=warehouse1,
|
||||
item_code=item_code,
|
||||
qty=20,
|
||||
company=pr.company,
|
||||
)
|
||||
|
||||
ste7.reload()
|
||||
self.assertEqual(ste7.items[0].valuation_rate, 275.00)
|
||||
|
||||
create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1)
|
||||
|
||||
pr.reload()
|
||||
@@ -1964,6 +1908,81 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
ste5.reload()
|
||||
self.assertEqual(ste5.items[0].valuation_rate, valuation_rate)
|
||||
|
||||
ste6.reload()
|
||||
self.assertEqual(ste6.items[0].valuation_rate, valuation_rate)
|
||||
|
||||
ste7.reload()
|
||||
self.assertEqual(ste7.items[0].valuation_rate, valuation_rate)
|
||||
|
||||
def test_valuation_rate_in_return_purchase_receipt_for_moving_average(self):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
|
||||
# Step - 1: Create an Item (Valuation Method = Moving Average)
|
||||
item_code = make_item(properties={"is_stock_item": 1, "valuation_method": "Moving Average"}).name
|
||||
|
||||
# Step - 2: Create a Purchase Receipt (Qty = 10, Rate = 100)
|
||||
pr = make_purchase_receipt(qty=10, rate=100, item_code=item_code)
|
||||
|
||||
# Step - 3: Create a Material Receipt Stock Entry (Qty = 100, Basic Rate = 10)
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
make_stock_entry(
|
||||
purpose="Material Receipt",
|
||||
item_code=item_code,
|
||||
to_warehouse=warehouse,
|
||||
qty=100,
|
||||
rate=10,
|
||||
)
|
||||
|
||||
# Step - 4: Create a Material Issue Stock Entry (Qty = 100, Basic Rate = 18.18 [Auto Fetched])
|
||||
make_stock_entry(
|
||||
purpose="Material Issue", item_code=item_code, from_warehouse=warehouse, qty=100
|
||||
)
|
||||
|
||||
# Step - 5: Create a Return Purchase Return (Qty = -8, Rate = 100 [Auto fetched])
|
||||
return_pr = make_purchase_receipt(
|
||||
is_return=1,
|
||||
return_against=pr.name,
|
||||
item_code=item_code,
|
||||
qty=-8,
|
||||
)
|
||||
|
||||
sle = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_no": return_pr.name, "voucher_detail_no": return_pr.items[0].name},
|
||||
["posting_date", "posting_time", "outgoing_rate", "valuation_rate"],
|
||||
as_dict=1,
|
||||
)
|
||||
previous_sle_valuation_rate = get_previous_sle(
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": warehouse,
|
||||
"posting_date": sle.posting_date,
|
||||
"posting_time": sle.posting_time,
|
||||
}
|
||||
).get("valuation_rate")
|
||||
|
||||
# Test - 1: Valuation Rate should be equal to Outgoing Rate
|
||||
self.assertEqual(flt(sle.outgoing_rate, 2), flt(sle.valuation_rate, 2))
|
||||
|
||||
# Test - 2: Valuation Rate should be equal to Previous SLE Valuation Rate
|
||||
self.assertEqual(flt(sle.valuation_rate, 2), flt(previous_sle_valuation_rate, 2))
|
||||
|
||||
def non_internal_transfer_purchase_receipt(self):
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
pr_doc = make_purchase_receipt(do_not_submit=True)
|
||||
warehouse = create_warehouse("Internal Transfer Warehouse", pr_doc.company)
|
||||
pr_doc.items[0].db_set("target_warehouse", "warehouse")
|
||||
|
||||
pr_doc.reload()
|
||||
|
||||
self.assertEqual(pr_doc.items[0].from_warehouse, warehouse.name)
|
||||
|
||||
pr_doc.save()
|
||||
pr_doc.reload()
|
||||
self.assertFalse(pr_doc.items[0].from_warehouse)
|
||||
|
||||
|
||||
def prepare_data_for_internal_transfer():
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import nowdate
|
||||
from frappe.utils.data import add_to_date, today
|
||||
|
||||
@@ -173,6 +173,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
|
||||
|
||||
riv.set_status("Skipped")
|
||||
|
||||
@change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
|
||||
def test_prevention_of_cancelled_transaction_riv(self):
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
|
||||
@@ -295,6 +296,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
|
||||
accounts_settings.acc_frozen_upto = ""
|
||||
accounts_settings.save()
|
||||
|
||||
@change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
|
||||
def test_create_repost_entry_for_cancelled_document(self):
|
||||
pr = make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory",
|
||||
|
||||
@@ -1414,6 +1414,7 @@ class TestStockEntry(FrappeTestCase):
|
||||
self.assertEqual(se.items[0].item_name, item.item_name)
|
||||
self.assertEqual(se.items[0].stock_uom, item.stock_uom)
|
||||
|
||||
@change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
|
||||
def test_reposting_for_depedent_warehouse(self):
|
||||
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_sl_entries
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"label": "Limit timeslot for Stock Reposting"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"default": "1",
|
||||
"fieldname": "item_based_reposting",
|
||||
"fieldtype": "Check",
|
||||
"label": "Use Item based reposting"
|
||||
@@ -57,7 +57,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-11-02 01:22:45.155841",
|
||||
"modified": "2023-11-01 16:14:29.080697",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reposting Settings",
|
||||
@@ -77,4 +77,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,6 +751,12 @@ def get_default_cost_center(args, item=None, item_group=None, brand=None, compan
|
||||
data = frappe.get_attr(path)(args.get("item_code"), company)
|
||||
|
||||
if data and (data.selling_cost_center or data.buying_cost_center):
|
||||
if args.get("customer") and data.selling_cost_center:
|
||||
return data.selling_cost_center
|
||||
|
||||
elif args.get("supplier") and data.buying_cost_center:
|
||||
return data.buying_cost_center
|
||||
|
||||
return data.selling_cost_center or data.buying_cost_center
|
||||
|
||||
if not cost_center and args.get("cost_center"):
|
||||
|
||||
@@ -33,5 +33,43 @@ frappe.query_reports["Stock and Account Value Comparison"] = {
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today(),
|
||||
},
|
||||
]
|
||||
],
|
||||
|
||||
get_datatable_options(options) {
|
||||
return Object.assign(options, {
|
||||
checkboxColumn: true,
|
||||
});
|
||||
},
|
||||
|
||||
onload(report) {
|
||||
report.page.add_inner_button(__("Create Reposting Entries"), function() {
|
||||
let message = `
|
||||
<div>
|
||||
<p>
|
||||
Reposting Entries will change the value of
|
||||
accounts Stock In Hand, and Stock Expenses
|
||||
in the Trial Balance report and will also change
|
||||
the Balance Value in the Stock Balance report.
|
||||
</p>
|
||||
<p>Are you sure you want to create Reposting Entries?</p>
|
||||
</div>`;
|
||||
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
|
||||
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
|
||||
|
||||
if (!selected_rows.length) {
|
||||
frappe.throw(__("Please select rows to create Reposting Entries"));
|
||||
}
|
||||
|
||||
frappe.confirm(__(message), () => {
|
||||
frappe.call({
|
||||
method: "erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison.create_reposting_entries",
|
||||
args: {
|
||||
rows: selected_rows,
|
||||
company: frappe.query_report.get_filter_values().company
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import get_link_to_form, parse_json
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.utils import get_currency_precision, get_stock_accounts
|
||||
@@ -134,3 +135,35 @@ def get_columns(filters):
|
||||
"width": "120",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_reposting_entries(rows, company):
|
||||
if isinstance(rows, str):
|
||||
rows = parse_json(rows)
|
||||
|
||||
entries = []
|
||||
for row in rows:
|
||||
row = frappe._dict(row)
|
||||
|
||||
try:
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Repost Item Valuation",
|
||||
"based_on": "Transaction",
|
||||
"status": "Queued",
|
||||
"voucher_type": row.voucher_type,
|
||||
"voucher_no": row.voucher_no,
|
||||
"posting_date": row.posting_date,
|
||||
"company": company,
|
||||
"allow_nagative_stock": 1,
|
||||
}
|
||||
).submit()
|
||||
|
||||
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
if entries:
|
||||
entries = ", ".join(entries)
|
||||
frappe.msgprint(_("Reposting entries created: {0}").format(entries))
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
/* eslint-disable */
|
||||
|
||||
const DIFFERNCE_FIELD_NAMES = [
|
||||
"difference_in_qty",
|
||||
"fifo_qty_diff",
|
||||
"fifo_value_diff",
|
||||
"fifo_valuation_diff",
|
||||
"valuation_diff",
|
||||
"fifo_difference_diff",
|
||||
"diff_value_diff"
|
||||
'difference_in_qty',
|
||||
'fifo_qty_diff',
|
||||
'fifo_value_diff',
|
||||
'fifo_valuation_diff',
|
||||
'valuation_diff',
|
||||
'fifo_difference_diff',
|
||||
'diff_value_diff'
|
||||
];
|
||||
|
||||
frappe.query_reports["Stock Ledger Invariant Check"] = {
|
||||
"filters": [
|
||||
frappe.query_reports['Stock Ledger Invariant Check'] = {
|
||||
'filters': [
|
||||
{
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Item",
|
||||
"mandatory": 1,
|
||||
"options": "Item",
|
||||
'fieldname': 'item_code',
|
||||
'fieldtype': 'Link',
|
||||
'label': 'Item',
|
||||
'mandatory': 1,
|
||||
'options': 'Item',
|
||||
get_query: function() {
|
||||
return {
|
||||
filters: {is_stock_item: 1, has_serial_no: 0}
|
||||
@@ -27,18 +27,61 @@ frappe.query_reports["Stock Ledger Invariant Check"] = {
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Warehouse",
|
||||
"mandatory": 1,
|
||||
"options": "Warehouse",
|
||||
'fieldname': 'warehouse',
|
||||
'fieldtype': 'Link',
|
||||
'label': 'Warehouse',
|
||||
'mandatory': 1,
|
||||
'options': 'Warehouse',
|
||||
}
|
||||
],
|
||||
|
||||
formatter (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
|
||||
value = "<span style='color:red'>" + value + "</span>";
|
||||
value = '<span style="color:red">' + value + '</span>';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
get_datatable_options(options) {
|
||||
return Object.assign(options, {
|
||||
checkboxColumn: true,
|
||||
});
|
||||
},
|
||||
|
||||
onload(report) {
|
||||
report.page.add_inner_button(__('Create Reposting Entry'), () => {
|
||||
let message = `
|
||||
<div>
|
||||
<p>
|
||||
Reposting Entry will change the value of
|
||||
accounts Stock In Hand, and Stock Expenses
|
||||
in the Trial Balance report and will also change
|
||||
the Balance Value in the Stock Balance report.
|
||||
</p>
|
||||
<p>Are you sure you want to create a Reposting Entry?</p>
|
||||
</div>`;
|
||||
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
|
||||
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
|
||||
|
||||
if (!selected_rows.length) {
|
||||
frappe.throw(__('Please select a row to create a Reposting Entry'));
|
||||
}
|
||||
else if (selected_rows.length > 1) {
|
||||
frappe.throw(__('Please select only one row to create a Reposting Entry'));
|
||||
}
|
||||
else {
|
||||
frappe.confirm(__(message), () => {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries',
|
||||
args: {
|
||||
rows: selected_rows,
|
||||
item_code: frappe.query_report.get_filter_values().item_code,
|
||||
warehouse: frappe.query_report.get_filter_values().warehouse,
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import get_link_to_form, parse_json
|
||||
|
||||
SLE_FIELDS = (
|
||||
"name",
|
||||
@@ -247,3 +249,35 @@ def get_columns():
|
||||
"label": "H - J",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_reposting_entries(rows, item_code=None, warehouse=None):
|
||||
if isinstance(rows, str):
|
||||
rows = parse_json(rows)
|
||||
|
||||
entries = []
|
||||
for row in rows:
|
||||
row = frappe._dict(row)
|
||||
|
||||
try:
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Repost Item Valuation",
|
||||
"based_on": "Item and Warehouse",
|
||||
"status": "Queued",
|
||||
"item_code": item_code or row.item_code,
|
||||
"warehouse": warehouse or row.warehouse,
|
||||
"posting_date": row.posting_date,
|
||||
"posting_time": row.posting_time,
|
||||
"allow_nagative_stock": 1,
|
||||
}
|
||||
).submit()
|
||||
|
||||
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
||||
except frappe.DuplicateEntryError:
|
||||
continue
|
||||
|
||||
if entries:
|
||||
entries = ", ".join(entries)
|
||||
frappe.msgprint(_("Reposting entries created: {0}").format(entries))
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
const DIFFERENCE_FIELD_NAMES = [
|
||||
"difference_in_qty",
|
||||
"fifo_qty_diff",
|
||||
"fifo_value_diff",
|
||||
"fifo_valuation_diff",
|
||||
"valuation_diff",
|
||||
"fifo_difference_diff",
|
||||
"diff_value_diff"
|
||||
];
|
||||
|
||||
frappe.query_reports["Stock Ledger Variance"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Item",
|
||||
"options": "Item",
|
||||
get_query: function() {
|
||||
return {
|
||||
filters: {is_stock_item: 1, has_serial_no: 0}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Warehouse",
|
||||
"options": "Warehouse",
|
||||
get_query: function() {
|
||||
return {
|
||||
filters: {is_group: 0, disabled: 0}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "difference_in",
|
||||
"fieldtype": "Select",
|
||||
"label": "Difference In",
|
||||
"options": [
|
||||
"",
|
||||
"Qty",
|
||||
"Value",
|
||||
"Valuation",
|
||||
],
|
||||
},
|
||||
{
|
||||
"fieldname": "include_disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Include Disabled",
|
||||
}
|
||||
],
|
||||
|
||||
formatter (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
|
||||
value = "<span style='color:red'>" + value + "</span>";
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
get_datatable_options(options) {
|
||||
return Object.assign(options, {
|
||||
checkboxColumn: true,
|
||||
});
|
||||
},
|
||||
|
||||
onload(report) {
|
||||
report.page.add_inner_button(__('Create Reposting Entries'), () => {
|
||||
let message = `
|
||||
<div>
|
||||
<p>
|
||||
Reposting Entries will change the value of
|
||||
accounts Stock In Hand, and Stock Expenses
|
||||
in the Trial Balance report and will also change
|
||||
the Balance Value in the Stock Balance report.
|
||||
</p>
|
||||
<p>Are you sure you want to create Reposting Entries?</p>
|
||||
</div>`;
|
||||
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
|
||||
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
|
||||
|
||||
if (!selected_rows.length) {
|
||||
frappe.throw(__("Please select rows to create Reposting Entries"));
|
||||
}
|
||||
|
||||
frappe.confirm(__(message), () => {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries',
|
||||
args: {
|
||||
rows: selected_rows,
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2023-09-20 10:44:19.414449",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letterhead": null,
|
||||
"modified": "2023-09-20 10:44:19.414449",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Ledger Variance",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Stock Ledger Entry",
|
||||
"report_name": "Stock Ledger Variance",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, flt
|
||||
|
||||
from erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check import (
|
||||
get_data as stock_ledger_invariant_check,
|
||||
)
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
|
||||
filters = frappe._dict(filters or {})
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Stock Ledger Entry"),
|
||||
"options": "Stock Ledger Entry",
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Data",
|
||||
"label": _("Posting Date"),
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Data",
|
||||
"label": _("Posting Time"),
|
||||
},
|
||||
{
|
||||
"fieldname": "creation",
|
||||
"fieldtype": "Data",
|
||||
"label": _("Creation"),
|
||||
},
|
||||
{
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Item"),
|
||||
"options": "Item",
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Warehouse"),
|
||||
"options": "Warehouse",
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Voucher Type"),
|
||||
"options": "DocType",
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": _("Voucher No"),
|
||||
"options": "voucher_type",
|
||||
},
|
||||
{
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Batch"),
|
||||
"options": "Batch",
|
||||
},
|
||||
{
|
||||
"fieldname": "use_batchwise_valuation",
|
||||
"fieldtype": "Check",
|
||||
"label": _("Batchwise Valuation"),
|
||||
},
|
||||
{
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": _("Qty Change"),
|
||||
},
|
||||
{
|
||||
"fieldname": "incoming_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": _("Incoming Rate"),
|
||||
},
|
||||
{
|
||||
"fieldname": "consumption_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": _("Consumption Rate"),
|
||||
},
|
||||
{
|
||||
"fieldname": "qty_after_transaction",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(A) Qty After Transaction"),
|
||||
},
|
||||
{
|
||||
"fieldname": "expected_qty_after_transaction",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(B) Expected Qty After Transaction"),
|
||||
},
|
||||
{
|
||||
"fieldname": "difference_in_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": _("A - B"),
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_queue",
|
||||
"fieldtype": "Data",
|
||||
"label": _("FIFO/LIFO Queue"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_queue_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(C) Total Qty in Queue"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_qty_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("A - C"),
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_value",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(D) Balance Stock Value"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_stock_value",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(E) Balance Stock Value in Queue"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_value_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("D - E"),
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_value_difference",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(F) Change in Stock Value"),
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_value_from_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(G) Sum of Change in Stock Value"),
|
||||
},
|
||||
{
|
||||
"fieldname": "diff_value_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("G - D"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_stock_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(H) Change in Stock Value (FIFO Queue)"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_difference_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("H - F"),
|
||||
},
|
||||
{
|
||||
"fieldname": "valuation_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(I) Valuation Rate"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_valuation_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(J) Valuation Rate as per FIFO"),
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_valuation_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("I - J"),
|
||||
},
|
||||
{
|
||||
"fieldname": "balance_value_by_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": _("(K) Valuation = Value (D) ÷ Qty (A)"),
|
||||
},
|
||||
{
|
||||
"fieldname": "valuation_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": _("I - K"),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_data(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
item_warehouse_map = get_item_warehouse_combinations(filters)
|
||||
|
||||
data = []
|
||||
if item_warehouse_map:
|
||||
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
|
||||
|
||||
for item_warehouse in item_warehouse_map:
|
||||
report_data = stock_ledger_invariant_check(item_warehouse)
|
||||
|
||||
if not report_data:
|
||||
continue
|
||||
|
||||
for row in report_data:
|
||||
if has_difference(row, precision, filters.difference_in):
|
||||
data.append(add_item_warehouse_details(row, item_warehouse))
|
||||
break
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_item_warehouse_combinations(filters: dict = None) -> dict:
|
||||
filters = frappe._dict(filters or {})
|
||||
|
||||
bin = frappe.qb.DocType("Bin")
|
||||
item = frappe.qb.DocType("Item")
|
||||
warehouse = frappe.qb.DocType("Warehouse")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(bin)
|
||||
.inner_join(item)
|
||||
.on(bin.item_code == item.name)
|
||||
.inner_join(warehouse)
|
||||
.on(bin.warehouse == warehouse.name)
|
||||
.select(
|
||||
bin.item_code,
|
||||
bin.warehouse,
|
||||
)
|
||||
.where((item.is_stock_item == 1) & (item.has_serial_no == 0) & (warehouse.is_group == 0))
|
||||
)
|
||||
|
||||
if filters.item_code:
|
||||
query = query.where(item.name == filters.item_code)
|
||||
if filters.warehouse:
|
||||
query = query.where(warehouse.name == filters.warehouse)
|
||||
if not filters.include_disabled:
|
||||
query = query.where((item.disabled == 0) & (warehouse.disabled == 0))
|
||||
|
||||
return query.run(as_dict=1)
|
||||
|
||||
|
||||
def has_difference(row, precision, difference_in):
|
||||
has_qty_difference = flt(row.difference_in_qty, precision) or flt(row.fifo_qty_diff, precision)
|
||||
has_value_difference = (
|
||||
flt(row.diff_value_diff, precision)
|
||||
or flt(row.fifo_value_diff, precision)
|
||||
or flt(row.fifo_difference_diff, precision)
|
||||
)
|
||||
has_valuation_difference = flt(row.valuation_diff, precision) or flt(
|
||||
row.fifo_valuation_diff, precision
|
||||
)
|
||||
|
||||
if difference_in == "Qty" and has_qty_difference:
|
||||
return True
|
||||
elif difference_in == "Value" and has_value_difference:
|
||||
return True
|
||||
elif difference_in == "Valuation" and has_valuation_difference:
|
||||
return True
|
||||
elif difference_in not in ["Qty", "Value", "Valuation"] and (
|
||||
has_qty_difference or has_value_difference or has_valuation_difference
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def add_item_warehouse_details(row, item_warehouse):
|
||||
row.update(
|
||||
{
|
||||
"item_code": item_warehouse.item_code,
|
||||
"warehouse": item_warehouse.warehouse,
|
||||
}
|
||||
)
|
||||
|
||||
return row
|
||||
@@ -278,6 +278,8 @@ def update_args_in_repost_item_valuation(
|
||||
frappe.publish_realtime(
|
||||
"item_reposting_progress",
|
||||
{"name": doc.name, "items_to_be_repost": json.dumps(args, default=str), "current_index": index},
|
||||
doctype=doc.doctype,
|
||||
docname=doc.name,
|
||||
)
|
||||
|
||||
|
||||
@@ -510,13 +512,28 @@ class update_entries_after(object):
|
||||
existing_sle_posting_date = (
|
||||
self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
|
||||
)
|
||||
|
||||
dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key)
|
||||
|
||||
if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
|
||||
val.sle_changed = True
|
||||
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
|
||||
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
|
||||
self.distinct_item_warehouses[key] = val
|
||||
self.new_items_found = True
|
||||
elif self.distinct_item_warehouses[key].get("reposting_status"):
|
||||
self.distinct_item_warehouses[key] = val
|
||||
elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos):
|
||||
# Future dependent voucher needs to be repost to get the correct stock value
|
||||
# If dependent voucher has not reposted, then add it to the list
|
||||
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
|
||||
self.new_items_found = True
|
||||
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
|
||||
self.distinct_item_warehouses[key] = val
|
||||
|
||||
def get_dependent_voucher_detail_nos(self, key):
|
||||
if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]:
|
||||
self.distinct_item_warehouses[key].dependent_voucher_detail_nos = []
|
||||
|
||||
return self.distinct_item_warehouses[key].dependent_voucher_detail_nos
|
||||
|
||||
def process_sle(self, sle):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
@@ -641,14 +658,16 @@ class update_entries_after(object):
|
||||
get_rate_for_return, # don't move this import to top
|
||||
)
|
||||
|
||||
rate = get_rate_for_return(
|
||||
sle.voucher_type,
|
||||
sle.voucher_no,
|
||||
sle.item_code,
|
||||
voucher_detail_no=sle.voucher_detail_no,
|
||||
sle=sle,
|
||||
)
|
||||
|
||||
if self.valuation_method == "Moving Average":
|
||||
rate = flt(self.data[self.args.warehouse].previous_sle.valuation_rate)
|
||||
else:
|
||||
rate = get_rate_for_return(
|
||||
sle.voucher_type,
|
||||
sle.voucher_no,
|
||||
sle.item_code,
|
||||
voucher_detail_no=sle.voucher_detail_no,
|
||||
sle=sle,
|
||||
)
|
||||
elif (
|
||||
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
||||
and sle.voucher_detail_no
|
||||
@@ -1162,6 +1181,7 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
|
||||
"qty_after_transaction",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"voucher_detail_no",
|
||||
"timestamp(posting_date, posting_time) as timestamp",
|
||||
],
|
||||
as_dict=1,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
{% for item, taxes in itemised_tax.items() %}
|
||||
<tr>
|
||||
<td>{{ item }}</td>
|
||||
<td class='text-right'>
|
||||
<td class="text-right">
|
||||
{% if doc.get('is_return') %}
|
||||
{{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }}
|
||||
{% else %}
|
||||
@@ -25,7 +25,7 @@
|
||||
{% for tax_account in tax_accounts %}
|
||||
{% set tax_details = taxes.get(tax_account) %}
|
||||
{% if tax_details %}
|
||||
<td class='text-right'>
|
||||
<td class="text-right">
|
||||
{% if tax_details.tax_rate or not tax_details.tax_amount %}
|
||||
({{ tax_details.tax_rate }}%)
|
||||
{% endif %}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% if d.thumbnail or d.image %}
|
||||
{{ product_image(d.thumbnail or d.image, no_border=True) }}
|
||||
{% else %}
|
||||
<div class="no-image-cart-item" style="min-height: 100px;">
|
||||
<div class="no-image-cart-item" style="min-height: 50px;">
|
||||
{{ frappe.utils.get_abbr(d.item_name) or "NA" }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -81,7 +81,7 @@ rfq = Class.extend({
|
||||
doc: doc
|
||||
},
|
||||
btn: this,
|
||||
callback: function(r){
|
||||
callback: function(r) {
|
||||
frappe.unfreeze();
|
||||
if(r.message){
|
||||
$('.btn-sm').hide()
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
{% from "erpnext/templates/includes/macros.html" import product_image_square, product_image %}
|
||||
|
||||
{% macro item_name_and_description(d, doc) %}
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
{{ product_image(d.image) }}
|
||||
</div>
|
||||
<div class="col-9">
|
||||
{{ d.item_code }}
|
||||
<p class="text-muted small">{{ d.description }}</p>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
{% if d.image %}
|
||||
{{ product_image(d.image) }}
|
||||
{% else %}
|
||||
<div class="website-image h-100 w-100" style="background-color:var(--gray-100);text-align: center;line-height: 3.6;">
|
||||
{{ frappe.utils.get_abbr(d.item_name)}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-9">
|
||||
{{ d.item_code }}
|
||||
<p class="text-muted small">{{ d.description }}</p>
|
||||
{% set supplier_part_no = frappe.db.get_value("Item Supplier", {'parent': d.item_code, 'supplier': doc.supplier}, "supplier_part_no") %}
|
||||
<p class="text-muted small supplier-part-no">
|
||||
{% if supplier_part_no %}
|
||||
{{_("Supplier Part No") + ": "+ supplier_part_no}}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -153,7 +153,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if attachments %}
|
||||
<div class="order-item-table">
|
||||
<div class="row order-items order-item-header text-muted">
|
||||
@@ -181,6 +180,7 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block script %}
|
||||
<script> {% include "templates/pages/order.js" %} </script>
|
||||
<script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "templates/web.html" %}
|
||||
|
||||
{% block header %}
|
||||
<h1>{{ doc.name }}</h1>
|
||||
<h1 style="margin-top: 10px;">{{ doc.name }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
@@ -16,7 +16,7 @@
|
||||
{% if doc.items %}
|
||||
<button class="btn btn-primary btn-sm"
|
||||
type="button">
|
||||
{{ _("Submit") }}</button>
|
||||
{{ _("Make Quotation") }}</button>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ Advertising,Werbung,
|
||||
Aerospace,Luft- und Raumfahrt,
|
||||
Against,Zu,
|
||||
Against Account,Gegenkonto,
|
||||
Against Journal Entry {0} does not have any unmatched {1} entry,"""Zu Buchungssatz"" {0} hat nur abgeglichene {1} Buchungen",
|
||||
Against Journal Entry {0} does not have any unmatched {1} entry,Buchungssatz {0} hat keinen offenen Eintrag auf der {1}-Seite,
|
||||
Against Journal Entry {0} is already adjusted against some other voucher,"""Zu Buchungssatz"" {0} ist bereits mit einem anderen Beleg abgeglichen",
|
||||
Against Supplier Invoice {0} dated {1},Zu Eingangsrechnung {0} vom {1},
|
||||
Against Voucher,Gegenbeleg,
|
||||
@@ -209,10 +209,10 @@ Amount of Integrated Tax,Betrag der integrierten Steuer,
|
||||
Amount of TDS Deducted,Betrag der abgezogenen TDS,
|
||||
Amount should not be less than zero.,Betrag sollte nicht kleiner als Null sein.,
|
||||
Amount to Bill,Rechnungsbetrag,
|
||||
Amount {0} {1} against {2} {3},Menge {0} {1} gegen {2} {3},
|
||||
Amount {0} {1} deducted against {2},Menge {0} {1} abgezogen gegen {2},
|
||||
Amount {0} {1} transferred from {2} to {3},Menge {0} {1} übertragen von {2} auf {3},
|
||||
Amount {0} {1} {2} {3},Menge {0} {1} {2} {3},
|
||||
Amount {0} {1} against {2} {3},Betrag {0} {1} gegen {2} {3},
|
||||
Amount {0} {1} deducted against {2},Betrag {0} {1} abgezogen gegen {2},
|
||||
Amount {0} {1} transferred from {2} to {3},Betrag {0} {1} wurde von {2} zu {3} transferiert,
|
||||
Amount {0} {1} {2} {3},Betrag {0} {1} {2} {3},
|
||||
Amt,Menge,
|
||||
"An Item Group exists with same name, please change the item name or rename the item group",Eine Artikelgruppe mit dem gleichen Namen existiert bereits. Bitte den Artikelnamen ändern oder die Artikelgruppe umbenennen,
|
||||
An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.,"Ein Semester mit ""Semesterjahr""'{0} und ""Semesternamen"" {1} ist bereits vorhanden. Bitte ändern Sie diese entsprechend und versuchen Sie es erneut.",
|
||||
@@ -281,7 +281,7 @@ Asset Name,Name Vermögenswert,
|
||||
Asset Received But Not Billed,"Vermögenswert erhalten, aber nicht in Rechnung gestellt",
|
||||
Asset Value Adjustment,Anpassung Vermögenswert,
|
||||
"Asset cannot be cancelled, as it is already {0}","Vermögenswert kann nicht rückgängig gemacht werden, da es ohnehin schon {0} ist",
|
||||
Asset scrapped via Journal Entry {0},Vermögenswert über Journaleintrag {0} entsorgt,
|
||||
Asset scrapped via Journal Entry {0},Vermögenswert über Buchungssatz {0} entsorgt,
|
||||
"Asset {0} cannot be scrapped, as it is already {1}",Anlagewert-{0} ist bereits entsorgt {1},
|
||||
Asset {0} does not belong to company {1},Vermögenswert {0} gehört nicht zu Unternehmen {1}.,
|
||||
Asset {0} must be submitted,Vermögenswert {0} muss eingereicht werden.,
|
||||
@@ -369,7 +369,7 @@ Base,Basis,
|
||||
Base URL,Basis-URL,
|
||||
Based On,Basiert auf,
|
||||
Based On Payment Terms,Basierend auf Zahlungsbedingungen,
|
||||
Basic,Grundeinkommen,
|
||||
Basic,Basic,
|
||||
Batch,Charge,
|
||||
Batch Entries,Batch-Einträge,
|
||||
Batch ID is mandatory,Batch-ID ist obligatorisch,
|
||||
@@ -442,7 +442,7 @@ Can be approved by {0},Kann von {0} genehmigt werden,
|
||||
"Can not filter based on Account, if grouped by Account","Wenn nach Konto gruppiert wurde, kann nicht auf Grundlage des Kontos gefiltert werden.",
|
||||
"Can not filter based on Voucher No, if grouped by Voucher","Wenn nach Beleg gruppiert wurde, kann nicht auf Grundlage von Belegen gefiltert werden.",
|
||||
"Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Kann den entlassenen Krankenhauskatheter nicht markieren, es gibt keine fakturierten Rechnungen {0}",
|
||||
Can only make payment against unbilled {0},Zahlung kann nur zu einer noch nicht abgerechneten {0} erstellt werden,
|
||||
Can only make payment against unbilled {0},Zahlung kann nur zu einem noch nicht abgerechneten Beleg vom Typ {0} erstellt werden,
|
||||
Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',"Kann sich nur auf eine Zeile beziehen, wenn die Berechnungsart der Kosten entweder ""auf vorherige Zeilensumme"" oder ""auf vorherigen Zeilenbetrag"" ist",
|
||||
"Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Kann die Bewertungsmethode nicht ändern, da es Transaktionen gegen einige Posten gibt, für die es keine eigene Bewertungsmethode gibt",
|
||||
Can't create standard criteria. Please rename the criteria,Kann keine Standardkriterien erstellen. Bitte benennen Sie die Kriterien um,
|
||||
@@ -450,7 +450,7 @@ Cancel,Abbrechen,
|
||||
Cancel Material Visit {0} before cancelling this Warranty Claim,Materialkontrolle {0} stornieren vor Abbruch dieses Garantieantrags,
|
||||
Cancel Material Visits {0} before cancelling this Maintenance Visit,Materialkontrolle {0} stornieren vor Abbruch dieses Wartungsbesuchs,
|
||||
Cancel Subscription,Abonnement beenden,
|
||||
Cancel the journal entry {0} first,Brechen Sie zuerst den Journaleintrag {0} ab,
|
||||
Cancel the journal entry {0} first,Brechen Sie zuerst den Buchungssatz {0} ab,
|
||||
Canceled,Abgebrochen,
|
||||
"Cannot Submit, Employees left to mark attendance","Kann nicht übergeben werden, Mitarbeiter sind zur Teilnahme zugelassen",
|
||||
Cannot be a fixed asset item as Stock Ledger is created.,"Kann keine Anlageposition sein, wenn das Stock Ledger erstellt wird.",
|
||||
@@ -474,11 +474,13 @@ Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',"Kann ni
|
||||
"Cannot delete Serial No {0}, as it is used in stock transactions","Die Seriennummer {0} kann nicht gelöscht werden, da sie in Lagertransaktionen verwendet wird",
|
||||
Cannot enroll more than {0} students for this student group.,Kann nicht mehr als {0} Studenten für diese Studentengruppe einschreiben.,
|
||||
Cannot find active Leave Period,Aktive Abwesenheitszeit kann nicht gefunden werden,
|
||||
Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über Kundenaufträge bestellte Stückzahl {1}",
|
||||
Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über den Auftrag bestellte Stückzahl {1}",
|
||||
Cannot pay to Customer without any negative outstanding invoice,"Es kann nicht an den Kunden gezahlt werden, ohne dass eine Gutschrift vorhanden ist",
|
||||
Cannot promote Employee with status Left,Mitarbeiter mit Status "Links" kann nicht gefördert werden,
|
||||
Cannot receive from Supplier without any negative outstanding invoice,"Es kann nicht vom Lieferanten empfangen werden, ohne dass eine Gutschrift vorhanden ist",
|
||||
Cannot refer row number greater than or equal to current row number for this Charge type,"Für diese Berechnungsart kann keine Zeilennummern zugeschrieben werden, die größer oder gleich der aktuellen Zeilennummer ist",
|
||||
Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf ""bezogen auf Menge der vorhergenden Zeile"" oder auf ""bezogen auf Gesamtmenge der vorhergenden Zeile"" gesetzt werden",
|
||||
Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Kundenauftrag dazu existiert.",
|
||||
Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf „Bezogen auf Betrag der vorhergenden Zeile“ oder auf „Bezogen auf Gesamtbetrag der vorhergenden Zeilen“ gesetzt werden",
|
||||
Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Auftrag dazu existiert.",
|
||||
Cannot set authorization on basis of Discount for {0},Genehmigung kann nicht auf der Basis des Rabattes für {0} festgelegt werden,
|
||||
Cannot set multiple Item Defaults for a company.,Es können nicht mehrere Artikelstandards für ein Unternehmen festgelegt werden.,
|
||||
Cannot set quantity less than delivered quantity,Menge kann nicht kleiner als gelieferte Menge sein,
|
||||
@@ -514,7 +516,7 @@ Change Template Code,Vorlagencode ändern,
|
||||
Changing Customer Group for the selected Customer is not allowed.,Die Änderung der Kundengruppe für den ausgewählten Kunden ist nicht zulässig.,
|
||||
Chapter,Gruppe,
|
||||
Chapter information.,Gruppeninformation,
|
||||
Charge of type 'Actual' in row {0} cannot be included in Item Rate,"Kosten für den Typ ""real"" in Zeile {0} können nicht in den Artikelpreis mit eingeschlossen werden",
|
||||
Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,"Kosten für den Typ „Tatsächlich“ in Zeile {0} können nicht in den Artikelpreis oder den bezahlen Betrag einfließen",
|
||||
Chargeble,Belastung,
|
||||
Charges are updated in Purchase Receipt against each item,Kosten werden im Kaufbeleg für jede Position aktualisiert,
|
||||
"Charges will be distributed proportionately based on item qty or amount, as per your selection",Die Kosten werden gemäß Ihrer Wahl anteilig verteilt basierend auf Artikelmenge oder -preis,
|
||||
@@ -657,7 +659,7 @@ Create Inter Company Journal Entry,Erstellen Sie einen Inter Company Journal Ein
|
||||
Create Invoice,Rechnung erstellen,
|
||||
Create Invoices,Rechnungen erstellen,
|
||||
Create Job Card,Jobkarte erstellen,
|
||||
Create Journal Entry,Journaleintrag erstellen,
|
||||
Create Journal Entry,Buchungssatz erstellen,
|
||||
Create Lead,Lead erstellen,
|
||||
Create Leads,Leads erstellen,
|
||||
Create Maintenance Visit,Wartungsbesuch anlegen,
|
||||
@@ -837,7 +839,7 @@ Did not find any item called {0},Hat keinen Artikel finden genannt {0},
|
||||
Diff Qty,Diff Menge,
|
||||
Difference Account,Differenzkonto,
|
||||
"Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Differenzkonto muss ein Vermögens-/Verbindlichkeiten-Konto sein, da dieser Lagerabgleich eine Eröffnungsbuchung ist",
|
||||
Difference Amount,Differenzmenge,
|
||||
Difference Amount,Differenzbetrag,
|
||||
Difference Amount must be zero,Differenzbetrag muss Null sein,
|
||||
Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,"Unterschiedliche Maßeinheiten für Artikel führen zu falschen Werten für das (Gesamt-)Nettogewicht. Es muss sicher gestellt sein, dass das Nettogewicht jedes einzelnen Artikels in der gleichen Maßeinheit angegeben ist.",
|
||||
Direct Expenses,Direkte Aufwendungen,
|
||||
@@ -1032,7 +1034,7 @@ Fieldname,Feldname,
|
||||
Fields,Felder,
|
||||
Fill the form and save it,Formular ausfüllen und speichern,
|
||||
Filter Employees By (Optional),Mitarbeiter filtern nach (optional),
|
||||
"Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile # {0}: Feldname <b>{1}</b> muss vom Typ "Link" oder "Tabelle MultiSelect" sein,
|
||||
"Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile {0}: Feldname <b>{1}</b> muss vom Typ "Link" oder "Tabelle MultiSelect" sein,
|
||||
Filter Total Zero Qty,Gesamtmenge filtern,
|
||||
Finance Book,Finanzbuch,
|
||||
Financial / accounting year.,Finanz-/Rechnungsjahr,
|
||||
@@ -1306,6 +1308,7 @@ Invalid GSTIN! A GSTIN must have 15 characters.,Ungültige GSTIN! Eine GSTIN mus
|
||||
Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,Ungültige GSTIN! Die ersten beiden Ziffern von GSTIN sollten mit der Statusnummer {0} übereinstimmen.,
|
||||
Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,Ungültige GSTIN! Die von Ihnen eingegebene Eingabe stimmt nicht mit dem Format von GSTIN überein.,
|
||||
Invalid Posting Time,Ungültige Buchungszeit,
|
||||
Invalid Purchase Invoice,Ungültige Einkaufsrechnung,
|
||||
Invalid attribute {0} {1},Ungültiges Attribut {0} {1},
|
||||
Invalid quantity specified for item {0}. Quantity should be greater than 0.,Ungültzige Anzahl für Artikel {0} angegeben. Anzahl sollte größer als 0 sein.,
|
||||
Invalid reference {0} {1},Ungültige Referenz {0} {1},
|
||||
@@ -1771,7 +1774,7 @@ Odometer,Tacho,
|
||||
Office Equipments,Büroausstattung,
|
||||
Office Maintenance Expenses,Büro-Wartungskosten,
|
||||
Office Rent,Büromiete,
|
||||
On Hold,In Wartestellung,
|
||||
On Hold,Auf Eis gelegt,
|
||||
On Net Total,Auf Nettosumme,
|
||||
One customer can be part of only single Loyalty Program.,Ein Kunde kann Teil eines einzigen Treueprogramms sein.,
|
||||
Online Auctions,Online-Auktionen,
|
||||
@@ -1861,7 +1864,7 @@ Packing Slip,Packzettel,
|
||||
Packing Slip(s) cancelled,Packzettel storniert,
|
||||
Paid,Bezahlt,
|
||||
Paid Amount,Gezahlter Betrag,
|
||||
Paid Amount cannot be greater than total negative outstanding amount {0},Gezahlten Betrag kann nicht größer sein als die Gesamt negativ ausstehenden Betrag {0},
|
||||
Paid Amount cannot be greater than total negative outstanding amount {0},"Der gezahlte Betrag darf nicht größer sein als der gesamte, negative, ausstehende Betrag {0}",
|
||||
Paid amount + Write Off Amount can not be greater than Grand Total,Summe aus gezahltem Betrag + ausgebuchter Betrag darf nicht größer der Gesamtsumme sein,
|
||||
Paid and Not Delivered,Bezahlt und nicht ausgeliefert,
|
||||
Parameter,Parameter,
|
||||
@@ -1914,7 +1917,7 @@ Payment Terms,Zahlungsbedingungen,
|
||||
Payment Terms Template,Vorlage Zahlungsbedingungen,
|
||||
Payment Terms based on conditions,Zahlungsbedingungen basieren auf Bedingungen,
|
||||
Payment Type,Zahlungsart,
|
||||
"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss eine der Receive sein, Pay und interne Übertragung",
|
||||
"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss entweder 'Empfangen', 'Zahlen' oder 'Interner Transfer' sein",
|
||||
Payment against {0} {1} cannot be greater than Outstanding Amount {2},Zahlung zu {0} {1} kann nicht größer als ausstehender Betrag {2} sein,
|
||||
Payment of {0} from {1} to {2},Zahlung von {0} von {1} an {2},
|
||||
Payment request {0} created,Zahlungsaufforderung {0} erstellt,
|
||||
@@ -2299,9 +2302,9 @@ Read blog,Blog lesen,
|
||||
Read the ERPNext Manual,Lesen Sie das ERPNext-Handbuch,
|
||||
Reading Uploaded File,Hochgeladene Datei lesen,
|
||||
Real Estate,Immobilien,
|
||||
Reason For Putting On Hold,Grund für das Halten,
|
||||
Reason for Hold,Grund für das Halten,
|
||||
Reason for hold: ,Grund für das Halten:,
|
||||
Reason For Putting On Hold,Grund für das auf Eis legen,
|
||||
Reason for Hold,Grund für das auf Eis legen,
|
||||
Reason for hold: ,Grund für das auf Eis legen:,
|
||||
Receipt,Kaufbeleg,
|
||||
Receipt document must be submitted,Eingangsbeleg muss vorgelegt werden,
|
||||
Receivable,Forderung,
|
||||
@@ -2321,18 +2324,20 @@ Ref Date,Ref-Datum,
|
||||
Reference,Referenz,
|
||||
Reference #{0} dated {1},Referenz #{0} vom {1},
|
||||
Reference Date,Referenzdatum,
|
||||
Reference Doctype must be one of {0},Referenz Doctype muss man von {0},
|
||||
Reference Doctype must be one of {0},Referenz-Typ muss eine von {0} sein,
|
||||
Reference Document,Referenzdokument,
|
||||
Reference Document Type,Referenz-Dokumententyp,
|
||||
Reference No & Reference Date is required for {0},Referenznr. & Referenz-Tag sind erforderlich für {0},
|
||||
Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum ist obligatorisch für Bankengeschäft,
|
||||
Reference No is mandatory if you entered Reference Date,"Referenznr. ist zwingend erforderlich, wenn Referenz-Tag eingegeben wurde",
|
||||
Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum sind Pflichtfelder,
|
||||
Reference No is mandatory if you entered Reference Date,"Referenznummer ist ein Pflichtfeld, wenn ein Referenzdatum eingegeben wurde",
|
||||
Reference No.,Referenznummer.,
|
||||
Reference Number,Referenznummer,
|
||||
Reference Owner,Referenz Besitzer,
|
||||
Reference Type,Referenz-Typ,
|
||||
"Reference: {0}, Item Code: {1} and Customer: {2}","Referenz: {0}, Item Code: {1} und Kunde: {2}",
|
||||
References,Referenzen,
|
||||
References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,"Die Referenzen {0} vom Typ {1} hatten keinen ausstehenden Betrag mehr, bevor die Zahlung gebucht wurde. Jetzt haben sie einen negativen ausstehenden Betrag.",
|
||||
If this is undesirable please cancel the corresponding Payment Entry.,"Falls dies nicht erwünscht ist, stornieren Sie bitte die entsprechende Zahlung.",
|
||||
Refresh Token,Aktualisieren Token,
|
||||
Region,Region,
|
||||
Register,Neu registrieren,
|
||||
@@ -2410,7 +2415,7 @@ Return,Zurück,
|
||||
Return / Credit Note,Return / Gutschrift,
|
||||
Return / Debit Note,Return / Lastschrift,
|
||||
Returns,Retouren,
|
||||
Reverse Journal Entry,Journaleintrag umkehren,
|
||||
Reverse Journal Entry,Buchungssatz umkehren,
|
||||
Review Invitation Sent,Einladung überprüfen gesendet,
|
||||
Review and Action,Überprüfung und Aktion,
|
||||
Role,Rolle,
|
||||
@@ -2423,63 +2428,64 @@ Root cannot have a parent cost center,Root kann keine übergeordnete Kostenstell
|
||||
Round Off,Abschliessen,
|
||||
Rounded Total,Gerundete Gesamtsumme,
|
||||
Route,Route,
|
||||
Row # {0}: ,Zeile # {0}:,
|
||||
Row # {0}: Batch No must be same as {1} {2},Zeile # {0}: Chargennummer muss dieselbe sein wie {1} {2},
|
||||
Row # {0}: Cannot return more than {1} for Item {2},Zeile # {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden,
|
||||
Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Row # {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}",
|
||||
Row # {0}: Serial No is mandatory,Zeile # {0}: Seriennummer ist zwingend erforderlich,
|
||||
Row # {0}: Serial No {1} does not match with {2} {3},Zeile # {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein,
|
||||
Row #{0} (Payment Table): Amount must be negative,Zeilennr. {0} (Zahlungstabelle): Betrag muss negativ sein,
|
||||
Row #{0} (Payment Table): Amount must be positive,Zeile # {0} (Zahlungstabelle): Betrag muss positiv sein,
|
||||
Row #{0}: Account {1} does not belong to company {2},Zeile # {0}: Konto {1} gehört nicht zur Unternehmen {2},
|
||||
Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile # {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein.,
|
||||
"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile Nr. {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}",
|
||||
Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeilennr. {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.",
|
||||
Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Row # {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2},
|
||||
Row #{0}: Duplicate entry in References {1} {2},Zeile # {0}: Eintrag in Referenzen {1} {2} duplizieren,
|
||||
Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Row # {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein,
|
||||
Row #{0}: Item added,Zeile # {0}: Element hinzugefügt,
|
||||
Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Row # {0}: Journal Entry {1} nicht Konto {2} oder bereits abgestimmt gegen einen anderen Gutschein,
|
||||
Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile #{0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits ein Lieferantenauftrag vorhanden ist",
|
||||
Row #{0}: Please set reorder quantity,Zeile #{0}: Bitte Nachbestellmenge angeben,
|
||||
Row #{0}: Please specify Serial No for Item {1},Zeile #{0}: Bitte Seriennummer für Artikel {1} angeben,
|
||||
Row #{0}: Qty increased by 1,Zeile # {0}: Menge um 1 erhöht,
|
||||
Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile #{0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein,
|
||||
Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Row # {0}: Referenzdokumenttyp muss einer der Kostenansprüche oder des Journaleintrags sein,
|
||||
"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Row # {0}: Referenzdokumenttyp muss eine der Bestellung, Rechnung oder Kaufjournaleintrag sein",
|
||||
Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile #{0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden,
|
||||
Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Row # {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1},
|
||||
Row #{0}: Reqd by Date cannot be before Transaction Date,Zeilennr. {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen,
|
||||
Row #{0}: Set Supplier for item {1},Zeile #{0}: Lieferanten für Artikel {1} einstellen,
|
||||
Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile # {0}: Status muss {1} für Rechnungsrabatt {2} sein,
|
||||
"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile # {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen",
|
||||
Row #{0}: Timings conflicts with row {1},Zeile #{0}: Timing-Konflikte mit Zeile {1},
|
||||
Row #{0}: {1} can not be negative for item {2},Row # {0}: {1} kann für Artikel nicht negativ sein {2},
|
||||
Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile Nr. {0}: Betrag kann nicht größer als der ausstehende Betrag zur Aufwandsabrechnung {1} sein. Ausstehender Betrag ist {2},
|
||||
Row # {0}: ,Zeile {0}:,
|
||||
Row # {0}: Batch No must be same as {1} {2},Zeile {0}: Chargennummer muss dieselbe sein wie {1} {2},
|
||||
Row # {0}: Cannot return more than {1} for Item {2},Zeile {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden,
|
||||
Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Zeile {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}",
|
||||
Row # {0}: Serial No is mandatory,Zeile {0}: Seriennummer ist zwingend erforderlich,
|
||||
Row # {0}: Serial No {1} does not match with {2} {3},Zeile {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein,
|
||||
Row #{0} (Payment Table): Amount must be negative,Zeile {0} (Zahlungstabelle): Betrag muss negativ sein,
|
||||
Row #{0} (Payment Table): Amount must be positive,Zeile {0} (Zahlungstabelle): Betrag muss positiv sein,
|
||||
Row #{0}: Account {1} does not belong to company {2},Zeile {0}: Konto {1} gehört nicht zur Unternehmen {2},
|
||||
Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein.,
|
||||
"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}",
|
||||
Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeile {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.",
|
||||
Row #{0}: Cannot allocate more than {1} against payment term {2},Zeile {0}: Es kann nicht mehr als {1} zu Zahlungsbedingung {2} zugeordnet werden,
|
||||
Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Zeile {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2},
|
||||
Row #{0}: Duplicate entry in References {1} {2},Referenz {1} {2} in Zeile {0} kommt doppelt vor,
|
||||
Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Zeile {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein,
|
||||
Row #{0}: Item added,Zeile {0}: Element hinzugefügt,
|
||||
Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Zeile {0}: Buchungssatz {1} betrifft nicht Konto {2} oder bereits mit einem anderen Beleg verrechnet,
|
||||
Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile {0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist",
|
||||
Row #{0}: Please set reorder quantity,Zeile {0}: Bitte Nachbestellmenge angeben,
|
||||
Row #{0}: Please specify Serial No for Item {1},Zeile {0}: Bitte Seriennummer für Artikel {1} angeben,
|
||||
Row #{0}: Qty increased by 1,Zeile {0}: Menge um 1 erhöht,
|
||||
Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile {0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein,
|
||||
Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Zeile {0}: Referenzdokumenttyp muss entweder 'Auslagenabrechnung' oder 'Buchungssatz' sein,
|
||||
"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Zeile {0}: Referenzdokumenttyp muss eine der Bestellung, Eingangsrechnung oder Buchungssatz sein",
|
||||
Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile {0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden,
|
||||
Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Zeile {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1},
|
||||
Row #{0}: Reqd by Date cannot be before Transaction Date,Zeile {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen,
|
||||
Row #{0}: Set Supplier for item {1},Zeile {0}: Lieferanten für Artikel {1} einstellen,
|
||||
Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile {0}: Status muss {1} für Rechnungsrabatt {2} sein,
|
||||
"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen",
|
||||
Row #{0}: Timings conflicts with row {1},Zeile {0}: Timing-Konflikte mit Zeile {1},
|
||||
Row #{0}: {1} can not be negative for item {2},Zeile {0}: {1} kann für Artikel nicht negativ sein {2},
|
||||
Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile {0}: Der Betrag kann nicht größer als der ausstehende Betrag der Auslagenabrechnung {1} sein. Der ausstehende Betrag ist {2},
|
||||
Row {0} : Operation is required against the raw material item {1},Zeile {0}: Vorgang ist für die Rohmaterialposition {1} erforderlich,
|
||||
Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},Zeile {0} # Der zugewiesene Betrag {1} darf nicht größer sein als der nicht beanspruchte Betrag {2},
|
||||
Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},Zeile {0} # Artikel {1} kann nicht mehr als {2} gegen Bestellung {3} übertragen werden.,
|
||||
Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile Nr. {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag,
|
||||
Row {0}: Activity Type is mandatory.,Row {0}: Leistungsart ist obligatorisch.,
|
||||
Row {0}: Advance against Customer must be credit,Row {0}: Voraus gegen Kunde muss Kredit,
|
||||
Row {0}: Advance against Supplier must be debit,Row {0}: Voraus gegen Lieferant muss belasten werden,
|
||||
Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag,
|
||||
Row {0}: Activity Type is mandatory.,Zeile {0}: Leistungsart ist obligatorisch.,
|
||||
Row {0}: Advance against Customer must be credit,Zeile {0}: Voraus gegen Kunde muss Kredit,
|
||||
Row {0}: Advance against Supplier must be debit,Zeile {0}: Voraus gegen Lieferant muss belasten werden,
|
||||
Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2},Zeile {0}: Zugewiesener Betrag {1} muss kleiner oder gleich der Zahlungsmenge {2} sein,
|
||||
Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},Zeile {0}: Zugeteilte Menge {1} muss kleiner als oder gleich dem ausstehenden Betrag in Rechnung {2} sein,
|
||||
Row {0}: An Reorder entry already exists for this warehouse {1},Zeile {0}: Es gibt bereits eine Nachbestellungsbuchung für dieses Lager {1},
|
||||
Row {0}: Bill of Materials not found for the Item {1},Zeile {0}: Bill of Materials nicht für den Artikel gefunden {1},
|
||||
Row {0}: Conversion Factor is mandatory,Row {0}: Umrechnungsfaktor ist zwingend erfoderlich,
|
||||
Row {0}: Conversion Factor is mandatory,Zeile {0}: Umrechnungsfaktor ist zwingend erfoderlich,
|
||||
Row {0}: Cost center is required for an item {1},Zeile {0}: Kostenstelle ist für einen Eintrag {1} erforderlich,
|
||||
Row {0}: Credit entry can not be linked with a {1},Zeile {0}: Habenbuchung kann nicht mit ein(em) {1} verknüpft werden,
|
||||
Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Zeile {0}: Währung der Stückliste # {1} sollte der gewählten Währung entsprechen {2},
|
||||
Row {0}: Debit entry can not be linked with a {1},Zeile {0}: Sollbuchung kann nicht mit ein(em) {1} verknüpft werden,
|
||||
Row {0}: Depreciation Start Date is required,Zeile {0}: Das Abschreibungsstartdatum ist erforderlich,
|
||||
Row {0}: Enter location for the asset item {1},Zeile Nr. {0}: Geben Sie den Speicherort für das Vermögenswert {1} ein.,
|
||||
Row {0}: Enter location for the asset item {1},Zeile {0}: Geben Sie einen Ort für den Vermögenswert {1} ein.,
|
||||
Row {0}: Exchange Rate is mandatory,Zeile {0}: Wechselkurs ist erforderlich,
|
||||
Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Zeile {0}: Erwarteter Wert nach Nutzungsdauer muss kleiner als Brutto Kaufbetrag sein,
|
||||
Row {0}: From Time and To Time is mandatory.,Row {0}: Von Zeit und zu Zeit ist obligatorisch.,
|
||||
Row {0}: From Time and To Time is mandatory.,Zeile {0}: Von Zeit und zu Zeit ist obligatorisch.,
|
||||
Row {0}: From Time and To Time of {1} is overlapping with {2},Zeile {0}: Zeitüberlappung in {1} mit {2},
|
||||
Row {0}: From time must be less than to time,Zeile {0}: Von Zeit zu Zeit muss kleiner sein,
|
||||
Row {0}: Hours value must be greater than zero.,Row {0}: Stunden-Wert muss größer als Null sein.,
|
||||
Row {0}: Hours value must be greater than zero.,Zeile {0}: Stunden-Wert muss größer als Null sein.,
|
||||
Row {0}: Invalid reference {1},Zeile {0}: Ungültige Referenz {1},
|
||||
Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Gruppe / Konto stimmt nicht mit {1} / {2} in {3} {4} überein,
|
||||
Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Gruppen-Typ und Gruppe sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich,
|
||||
@@ -2875,7 +2881,7 @@ Supplier Group master.,Lieferantengruppenstamm,
|
||||
Supplier Id,Lieferanten-ID,
|
||||
Supplier Invoice Date cannot be greater than Posting Date,Lieferant Rechnungsdatum kann nicht größer sein als Datum der Veröffentlichung,
|
||||
Supplier Invoice No,Lieferantenrechnungsnr.,
|
||||
Supplier Invoice No exists in Purchase Invoice {0},Lieferantenrechnung existiert in Kauf Rechnung {0},
|
||||
Supplier Invoice No exists in Purchase Invoice {0},Die Rechnungsnummer des Lieferanten wurde bereits in Eingangsrechnung {0} verwendet,
|
||||
Supplier Name,Lieferantenname,
|
||||
Supplier Part No,Lieferant Teile-Nr,
|
||||
Supplier Quotation,Lieferantenangebot,
|
||||
@@ -2935,7 +2941,7 @@ Template of terms or contract.,Vorlage für Geschäftsbedingungen oder Vertrag,
|
||||
Templates of supplier scorecard criteria.,Vorlagen der Lieferanten-Scorecard-Kriterien.,
|
||||
Templates of supplier scorecard variables.,Vorlagen der Lieferanten-Scorecard-Variablen.,
|
||||
Templates of supplier standings.,Vorlagen der Lieferantenwertung.,
|
||||
Temporarily on Hold,Vorübergehend in der Warteschleife,
|
||||
Temporarily on Hold,Vorübergehend auf Eis gelegt,
|
||||
Temporary,Temporär,
|
||||
Temporary Accounts,Temporäre Konten,
|
||||
Temporary Opening,Temporäre Eröffnungskonten,
|
||||
@@ -3077,7 +3083,7 @@ Total Budget,Gesamtbudget; Gesamtetat,
|
||||
Total Collected: {0},Gesammelt gesammelt: {0},
|
||||
Total Commission,Gesamtprovision,
|
||||
Total Contribution Amount: {0},Gesamtbeitragsbetrag: {0},
|
||||
Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Journaleintrag sein,
|
||||
Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Buchungssatz sein,
|
||||
Total Debit must be equal to Total Credit. The difference is {0},Gesamt-Soll muss gleich Gesamt-Haben sein. Die Differenz ist {0},
|
||||
Total Deduction,Gesamtabzug,
|
||||
Total Invoiced Amount,Gesamtrechnungsbetrag,
|
||||
@@ -3410,7 +3416,7 @@ on,Am,
|
||||
{0} is not in Optional Holiday List,{0} befindet sich nicht in der optionalen Feiertagsliste,
|
||||
{0} is not in a valid Payroll Period,{0} befindet sich nicht in einer gültigen Abrechnungsperiode,
|
||||
{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.,"{0} ist jetzt das Standardgeschäftsjahr. Bitte aktualisieren Sie Ihren Browser, damit die Änderungen wirksam werden.",
|
||||
{0} is on hold till {1},{0} ist zurückgestellt bis {1},
|
||||
{0} is on hold till {1},{0} ist auf Eis gelegt bis {1},
|
||||
{0} item found.,{0} Artikel gefunden.,
|
||||
{0} items found.,{0} Artikel gefunden.,
|
||||
{0} items in progress,{0} Elemente in Bearbeitung,
|
||||
@@ -3430,6 +3436,8 @@ on,Am,
|
||||
{0} variants created.,{0} Varianten erstellt.,
|
||||
{0} {1} created,{0} {1} erstellt,
|
||||
{0} {1} does not exist,{0} {1} existiert nicht,
|
||||
{0} {1} has already been fully paid.,{0} {1} wurde bereits vollständig bezahlt.,
|
||||
{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts.,"{0} {1} wurde bereits teilweise bezahlt. Bitte nutzen Sie den Button 'Ausstehende Rechnungen aufrufen', um die aktuell ausstehenden Beträge zu erhalten.",
|
||||
{0} {1} has been modified. Please refresh.,{0} {1} wurde geändert. Bitte aktualisieren.,
|
||||
{0} {1} has not been submitted so the action cannot be completed,"{0} {1} sind nicht gebucht, deshalb kann die Aktion nicht abgeschlossen werden",
|
||||
"{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} ist mit {2} verbunden, aber das Gegenkonto ist {3}",
|
||||
@@ -3441,9 +3449,10 @@ on,Am,
|
||||
{0} {1} is frozen,{0} {1} ist gesperrt,
|
||||
{0} {1} is fully billed,{0} {1} wird voll in Rechnung gestellt,
|
||||
{0} {1} is not active,{0} {1} ist nicht aktiv,
|
||||
{0} {1} is not associated with {2} {3},{0} {1} ist nicht mit {2} {3} verknüpft,
|
||||
{0} {1} is not associated with {2} {3},{0} {1} gehört nicht zu {2} {3},
|
||||
{0} {1} is not present in the parent company,{0} {1} ist in der Muttergesellschaft nicht vorhanden,
|
||||
{0} {1} is not submitted,{0} {1} wurde nicht übertragen,
|
||||
{0} {1} is on hold,{0} {1} liegt derzeit auf Eis,
|
||||
{0} {1} is {2},{0} {1} ist {2},
|
||||
{0} {1} must be submitted,{0} {1} muss vorgelegt werden,
|
||||
{0} {1} not in any active Fiscal Year.,{0} {1} nicht in einem aktiven Geschäftsjahr.,
|
||||
@@ -3575,7 +3584,7 @@ Account is not set for the dashboard chart {0},Konto ist nicht für das Dashboar
|
||||
Account {0} does not belong to company {1},Konto {0} gehört nicht zu Firma {1},
|
||||
Account {0} does not exists in the dashboard chart {1},Konto {0} ist im Dashboard-Diagramm {1} nicht vorhanden,
|
||||
Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry,Konto: <b>{0}</b> ist in Bearbeitung und kann von Journal Entry nicht aktualisiert werden,
|
||||
Account: {0} is not permitted under Payment Entry,Konto: {0} ist unter Zahlungseingang nicht zulässig,
|
||||
Account: {0} is not permitted under Payment Entry,Konto {0} kann nicht in Zahlung verwendet werden,
|
||||
Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.,Die Buchhaltungsdimension <b>{0}</b> ist für das Bilanzkonto <b>{1}</b> erforderlich.,
|
||||
Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.,Die Buchhaltungsdimension <b>{0}</b> ist für das Konto {1} "Gewinn und Verlust" erforderlich.,
|
||||
Accounting Masters,Accounting Masters,
|
||||
@@ -3639,6 +3648,8 @@ Billing Interval Count cannot be less than 1,Die Anzahl der Abrechnungsintervall
|
||||
Blue,Blau,
|
||||
Book,Buchen,
|
||||
Book Appointment,Einen Termin verabreden,
|
||||
Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,"Die Option 'Anzahlungen als Verbindlichkeit buchen' ist aktiviert. Das Ausgangskonto wurde von {0} auf {1} geändert.",
|
||||
Book Advance Payments in Separate Party Account,Anzahlungen als Verbindlichkeit buchen,
|
||||
Brand,Marke,
|
||||
Browse,Durchsuchen,
|
||||
Call Connected,Anruf verbunden,
|
||||
@@ -3860,6 +3871,7 @@ No correct answer is set for {0},Für {0} ist keine richtige Antwort festgelegt.
|
||||
No description,Keine Beschreibung,
|
||||
No issue has been raised by the caller.,Der Anrufer hat kein Problem angesprochen.,
|
||||
No items to publish,Keine Artikel zu veröffentlichen,
|
||||
No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,"Für {1} {2} wurden kein ausstehender Beleg vom Typ {0} gefunden, der den angegebenen Filtern entspricht.",
|
||||
No outstanding invoices found,Keine offenen Rechnungen gefunden,
|
||||
No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,"Für {0} {1} wurden keine ausstehenden Rechnungen gefunden, die die von Ihnen angegebenen Filter qualifizieren.",
|
||||
No outstanding invoices require exchange rate revaluation,Keine ausstehenden Rechnungen erfordern eine Neubewertung des Wechselkurses,
|
||||
@@ -3955,8 +3967,8 @@ Publish More Items,Veröffentlichen Sie weitere Elemente,
|
||||
Publish Your First Items,Veröffentlichen Sie Ihre ersten Artikel,
|
||||
Publish {0} Items,{0} Elemente veröffentlichen,
|
||||
Published Items,Veröffentlichte Artikel,
|
||||
Purchase Invoice cannot be made against an existing asset {0},Kaufrechnung kann nicht für ein vorhandenes Asset erstellt werden {0},
|
||||
Purchase Invoices,Rechnungen kaufen,
|
||||
Purchase Invoice cannot be made against an existing asset {0},Eingangsrechnung kann nicht für ein vorhandenes Asset erstellt werden {0},
|
||||
Purchase Invoices,Eingangsrechnungen,
|
||||
Purchase Orders,Kauforder,
|
||||
Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,"Der Kaufbeleg enthält keinen Artikel, für den die Option "Probe aufbewahren" aktiviert ist.",
|
||||
Purchase Return,Warenrücksendung,
|
||||
@@ -3995,20 +4007,20 @@ Review,Rezension,
|
||||
Room,Zimmer,
|
||||
Room Type,Zimmertyp,
|
||||
Row # ,Zeile #,
|
||||
Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile # {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein,
|
||||
Row #{0}: Cannot delete item {1} which has already been billed.,Zeile # {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden.,
|
||||
Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile # {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden",
|
||||
Row #{0}: Cannot delete item {1} which has already been received,"Zeile # {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden",
|
||||
Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile # {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.",
|
||||
Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile # {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.",
|
||||
Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile # {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden",
|
||||
Row #{0}: Cost Center {1} does not belong to company {2},Zeile # {0}: Kostenstelle {1} gehört nicht zu Firma {2},
|
||||
Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile # {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}.,
|
||||
Row #{0}: Payment document is required to complete the transaction,"Zeile # {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen",
|
||||
Row #{0}: Serial No {1} does not belong to Batch {2},Zeile # {0}: Seriennummer {1} gehört nicht zu Charge {2},
|
||||
Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile # {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen,
|
||||
Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile # {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein,
|
||||
Row #{0}: Service Start and End Date is required for deferred accounting,Zeile # {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich,
|
||||
Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein,
|
||||
Row #{0}: Cannot delete item {1} which has already been billed.,Zeile {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden.,
|
||||
Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden",
|
||||
Row #{0}: Cannot delete item {1} which has already been received,"Zeile {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden",
|
||||
Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.",
|
||||
Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.",
|
||||
Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden",
|
||||
Row #{0}: Cost Center {1} does not belong to company {2},Zeile {0}: Kostenstelle {1} gehört nicht zu Firma {2},
|
||||
Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}.,
|
||||
Row #{0}: Payment document is required to complete the transaction,"Zeile {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen",
|
||||
Row #{0}: Serial No {1} does not belong to Batch {2},Zeile {0}: Seriennummer {1} gehört nicht zu Charge {2},
|
||||
Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen,
|
||||
Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein,
|
||||
Row #{0}: Service Start and End Date is required for deferred accounting,Zeile {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich,
|
||||
Row {0}: Invalid Item Tax Template for item {1},Zeile {0}: Ungültige Artikelsteuervorlage für Artikel {1},
|
||||
Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Zeile {0}: Menge für {4} in Lager {1} zum Buchungszeitpunkt des Eintrags nicht verfügbar ({2} {3}),
|
||||
Row {0}: user has not applied the rule {1} on the item {2},Zeile {0}: Der Nutzer hat die Regel {1} nicht auf das Element {2} angewendet.,
|
||||
@@ -4275,7 +4287,7 @@ to,An,
|
||||
Cards,Karten,
|
||||
Percentage,Prozentsatz,
|
||||
Failed to setup defaults for country {0}. Please contact support@erpnext.com,Fehler beim Einrichten der Standardeinstellungen für Land {0}. Bitte wenden Sie sich an support@erpnext.com,
|
||||
Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile # {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben.,
|
||||
Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben.,
|
||||
Please set {0},Bitte {0} setzen,
|
||||
Please set {0},Bitte setzen Sie {0},supplier
|
||||
Draft,Entwurf,"docstatus,=,0"
|
||||
@@ -4362,7 +4374,7 @@ Row {0}: {1} is required in the expenses table to book an expense claim.,"Zeile
|
||||
Set the default account for the {0} {1},Legen Sie das Standardkonto für {0} {1} fest,
|
||||
(Half Day),(Halber Tag),
|
||||
Income Tax Slab,Einkommensteuerplatte,
|
||||
Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile # {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden,
|
||||
Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden,
|
||||
Row #{}: {} of {} should be {}. Please modify the account or select a different account.,Zeile # {}: {} von {} sollte {} sein. Bitte ändern Sie das Konto oder wählen Sie ein anderes Konto aus.,
|
||||
Row #{}: Please asign task to a member.,Zeile # {}: Bitte weisen Sie einem Mitglied eine Aufgabe zu.,
|
||||
Process Failed,Prozess fehlgeschlagen,
|
||||
@@ -4371,10 +4383,11 @@ Please set Warehouse in Woocommerce Settings,Bitte stellen Sie Warehouse in den
|
||||
Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same,Zeile {0}: Lieferlager ({1}) und Kundenlager ({2}) können nicht identisch sein,
|
||||
Row {0}: Due Date in the Payment Terms table cannot be before Posting Date,Zeile {0}: Fälligkeitsdatum in der Tabelle "Zahlungsbedingungen" darf nicht vor dem Buchungsdatum liegen,
|
||||
Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.,{} Für Element {} kann nicht gefunden werden. Bitte stellen Sie dasselbe in den Artikelstamm- oder Lagereinstellungen ein.,
|
||||
Row #{0}: The batch {1} has already expired.,Zeile # {0}: Der Stapel {1} ist bereits abgelaufen.,
|
||||
Row #{0}: The batch {1} has already expired.,Zeile {0}: Der Stapel {1} ist bereits abgelaufen.,
|
||||
Start Year and End Year are mandatory,Startjahr und Endjahr sind obligatorisch,
|
||||
GL Entry,Buchung zum Hauptbuch,
|
||||
Cannot allocate more than {0} against payment term {1},Es kann nicht mehr als {0} für die Zahlungsbedingung {1} zugeordnet werden.,
|
||||
Cannot receive from customer against negative outstanding,Negativer Gesamtbetrag kann nicht vom Kunden empfangen werden,
|
||||
The root account {0} must be a group,Das Root-Konto {0} muss eine Gruppe sein,
|
||||
Shipping rule not applicable for country {0} in Shipping Address,Versandregel gilt nicht für Land {0} in Versandadresse,
|
||||
Get Payments from,Zahlungen erhalten von,
|
||||
@@ -4517,7 +4530,7 @@ Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
|
||||
Over Billing Allowance (%),Mehr als Abrechnungsbetrag (%),
|
||||
Credit Controller,Kredit-Controller,
|
||||
Check Supplier Invoice Number Uniqueness,"Aktivieren, damit dieselbe Lieferantenrechnungsnummer nur einmal vorkommen kann",
|
||||
Make Payment via Journal Entry,Zahlung über Journaleintrag,
|
||||
Make Payment via Journal Entry,Zahlung über Buchungssatz,
|
||||
Unlink Payment on Cancellation of Invoice,Zahlung bei Stornierung der Rechnung aufheben,
|
||||
Book Asset Depreciation Entry Automatically,Vermögensabschreibung automatisch verbuchen,
|
||||
Automatically Add Taxes and Charges from Item Tax Template,Steuern und Gebühren aus Artikelsteuervorlage automatisch hinzufügen,
|
||||
@@ -4586,7 +4599,7 @@ Bank Statement Transaction Entry,Kontoauszug Transaktionseintrag,
|
||||
Bank Transaction Entries,Banktransaktionseinträge,
|
||||
New Transactions,Neue Transaktionen,
|
||||
Match Transaction to Invoices,Transaktion mit Rechnungen abgleichen,
|
||||
Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Journaleintrag,
|
||||
Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Buchungssatz,
|
||||
Submit/Reconcile Payments,Zahlungen absenden / abstimmen,
|
||||
Matching Invoices,Passende Rechnungen,
|
||||
Payment Invoice Items,Zahlung Rechnungspositionen,
|
||||
@@ -4839,6 +4852,7 @@ Account Paid To,Eingangskonto,
|
||||
Paid Amount (Company Currency),Gezahlter Betrag (Unternehmenswährung),
|
||||
Received Amount,erhaltenen Betrag,
|
||||
Received Amount (Company Currency),Erhaltene Menge (Gesellschaft Währung),
|
||||
Received Amount cannot be greater than Paid Amount,Der erhaltene Betrag darf nicht größer sein als der gezahlte Betrag,
|
||||
Get Outstanding Invoice,Erhalten Sie eine ausstehende Rechnung,
|
||||
Payment References,Bezahlung Referenzen,
|
||||
Writeoff,Abschreiben,
|
||||
@@ -4986,7 +5000,7 @@ Apply Tax Withholding Amount,Steuereinbehaltungsbetrag anwenden,
|
||||
Accounting Dimensions ,Buchhaltung Dimensionen,
|
||||
Supplier Invoice Details,Lieferant Rechnungsdetails,
|
||||
Supplier Invoice Date,Lieferantenrechnungsdatum,
|
||||
Return Against Purchase Invoice,Zurück zur Einkaufsrechnung,
|
||||
Return Against Purchase Invoice,Gutschrift zur Eingangsrechnung,
|
||||
Select Supplier Address,Lieferantenadresse auswählen,
|
||||
Contact Person,Kontaktperson,
|
||||
Select Shipping Address,Lieferadresse auswählen,
|
||||
@@ -5036,7 +5050,7 @@ Terms,Geschäftsbedingungen,
|
||||
Terms and Conditions1,Allgemeine Geschäftsbedingungen1,
|
||||
Group same items,Gruppe gleichen Artikel,
|
||||
Print Language,Drucksprache,
|
||||
"Once set, this invoice will be on hold till the set date","Einmal eingestellt, wird diese Rechnung bis zum festgelegten Datum gehalten",
|
||||
"Once set, this invoice will be on hold till the set date","Einmal eingestellt, liegt diese Rechnung bis zum festgelegten Datum auf Eis",
|
||||
Credit To,Gutschreiben auf,
|
||||
Party Account Currency,Gruppenkonten-Währung,
|
||||
Against Expense Account,Zu Aufwandskonto,
|
||||
@@ -5360,7 +5374,7 @@ Asset Owner,Eigentümer des Vermögenswertes,
|
||||
Asset Owner Company,Eigentümergesellschaft,
|
||||
Custodian,Depotbank,
|
||||
Disposal Date,Verkauf Datum,
|
||||
Journal Entry for Scrap,Journaleintrag für Ausschuss,
|
||||
Journal Entry for Scrap,Buchungssatz für Ausschuss,
|
||||
Available-for-use Date,Verfügbarkeitsdatum,
|
||||
Calculate Depreciation,Abschreibung berechnen,
|
||||
Allow Monthly Depreciation,Monatliche Abschreibung zulassen,
|
||||
@@ -5522,8 +5536,8 @@ Default Bank Account,Standardbankkonto,
|
||||
Is Transporter,Ist Transporter,
|
||||
Represents Company,Repräsentiert das Unternehmen,
|
||||
Supplier Type,Lieferantentyp,
|
||||
Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Kaufrechnungen ohne Bestellung zulassen,
|
||||
Allow Purchase Invoice Creation Without Purchase Receipt,Zulassen der Erstellung von Kaufrechnungen ohne Kaufbeleg,
|
||||
Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Eingangsrechnung ohne Bestellung zulassen,
|
||||
Allow Purchase Invoice Creation Without Purchase Receipt,Erstellen von Eingangsrechnung ohne Kaufbeleg ohne Kaufbeleg zulassen,
|
||||
Warn RFQs,Warnung Ausschreibungen,
|
||||
Warn POs,Warnen Sie POs,
|
||||
Prevent RFQs,Vermeidung von Ausschreibungen,
|
||||
@@ -7802,7 +7816,7 @@ Enable Perpetual Inventory,Permanente Inventur aktivieren,
|
||||
Default Inventory Account,Standard Inventurkonto,
|
||||
Stock Adjustment Account,Bestandskorrektur-Konto,
|
||||
Fixed Asset Depreciation Settings,Einstellungen Abschreibung von Anlagevermögen,
|
||||
Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Journaleintrag),
|
||||
Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Buchungssatz),
|
||||
Gain/Loss Account on Asset Disposal,Gewinn-/Verlustrechnung auf die Veräußerung von Vermögenswerten,
|
||||
Asset Depreciation Cost Center,Kostenstelle für Vermögenswertabschreibung,
|
||||
Budget Detail,Budget-Detail,
|
||||
@@ -8597,10 +8611,10 @@ Automatically Process Deferred Accounting Entry,Aufgeschobene Buchungsbuchung au
|
||||
Bank Clearance,Bankfreigabe,
|
||||
Bank Clearance Detail,Bankfreigabedetail,
|
||||
Update Cost Center Name / Number,Name / Nummer der Kostenstelle aktualisieren,
|
||||
Journal Entry Template,Journaleintragsvorlage,
|
||||
Journal Entry Template,Buchungssatz-Vorlage,
|
||||
Template Title,Vorlagentitel,
|
||||
Journal Entry Type,Journaleintragstyp,
|
||||
Journal Entry Template Account,Journaleintragsvorlagenkonto,
|
||||
Journal Entry Type,Buchungssatz-Typ,
|
||||
Journal Entry Template Account,Buchungssatzvorlagenkonto,
|
||||
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten,
|
||||
Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut,
|
||||
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
|
||||
@@ -8676,7 +8690,7 @@ Deferred Accounting Settings,Aufgeschobene Buchhaltungseinstellungen,
|
||||
Book Deferred Entries Based On,Buch verzögerte Einträge basierend auf,
|
||||
Days,Tage,
|
||||
Months,Monate,
|
||||
Book Deferred Entries Via Journal Entry,Buch verzögerte Einträge über Journaleintrag,
|
||||
Book Deferred Entries Via Journal Entry,Separaten Buchungssatz für latente Buchungen erstellen,
|
||||
Submit Journal Entries,Journaleinträge senden,
|
||||
If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually,"Wenn dieses Kontrollkästchen deaktiviert ist, werden Journaleinträge in einem Entwurfsstatus gespeichert und müssen manuell übermittelt werden",
|
||||
Enable Distributed Cost Center,Aktivieren Sie die verteilte Kostenstelle,
|
||||
@@ -8704,7 +8718,7 @@ Dunning Letter,Mahnbrief,
|
||||
Reference Detail No,Referenz Detail Nr,
|
||||
Custom Remarks,Benutzerdefinierte Bemerkungen,
|
||||
Please select a Company first.,Bitte wählen Sie zuerst eine Firma aus.,
|
||||
"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile # {0}: Der Referenzdokumenttyp muss Kundenauftrag, Verkaufsrechnung, Journaleintrag oder Mahnwesen sein",
|
||||
"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Buchungssatz oder Mahnung sein",
|
||||
POS Closing Entry,POS Closing Entry,
|
||||
POS Opening Entry,POS-Eröffnungseintrag,
|
||||
POS Transactions,POS-Transaktionen,
|
||||
@@ -8824,9 +8838,9 @@ Depreciation Posting Date,Buchungsdatum der Abschreibung,
|
||||
"By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ","Standardmäßig wird der Lieferantenname gemäß dem eingegebenen Lieferantennamen festgelegt. Wenn Sie möchten, dass Lieferanten von a benannt werden",
|
||||
choose the 'Naming Series' option.,Wählen Sie die Option "Naming Series".,
|
||||
Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.,Konfigurieren Sie die Standardpreisliste beim Erstellen einer neuen Kauftransaktion. Artikelpreise werden aus dieser Preisliste abgerufen.,
|
||||
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung oder einen Beleg erstellen können, ohne zuvor eine Bestellung zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Einkaufsrechnungen ohne Bestellung zulassen" im Lieferantenstamm aktiviert wird.",
|
||||
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung erstellen können, ohne zuvor einen Kaufbeleg zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen" im Lieferantenstamm aktiviert wird.",
|
||||
Quantity & Stock,Menge & Lager,
|
||||
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie eine Bestellung angelegt haben, bevor Sie eine Eingangsrechnung oder einen Kaufbeleg erfassen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Eingangsrechnungen ohne Bestellung zulassen' im Lieferantenstamm aktivieren.",
|
||||
"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie einen Kaufbeleg angelegt haben, bevor Sie eine Eingangsrechnung erfasen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen' im Lieferantenstamm aktivieren.",
|
||||
Quantity & Stock,Menge & Lager,
|
||||
Call Details,Anrufdetails,
|
||||
Authorised By,Authorisiert von,
|
||||
Signee (Company),Unterzeichner (Firma),
|
||||
@@ -9063,7 +9077,7 @@ Rented To Date,Bisher vermietet,
|
||||
Monthly Eligible Amount,Monatlicher förderfähiger Betrag,
|
||||
Total Eligible HRA Exemption,Insgesamt berechtigte HRA-Befreiung,
|
||||
Validating Employee Attendance...,Überprüfung der Mitarbeiterbeteiligung ...,
|
||||
Submitting Salary Slips and creating Journal Entry...,Einreichen von Gehaltsabrechnungen und Erstellen eines Journaleintrags ...,
|
||||
Submitting Salary Slips and creating Journal Entry...,Gehaltsabrechnungen werden gebucht und Buchungssätze erstellt...,
|
||||
Calculate Payroll Working Days Based On,Berechnen Sie die Arbeitstage der Personalabrechnung basierend auf,
|
||||
Consider Unmarked Attendance As,Betrachten Sie die nicht markierte Teilnahme als,
|
||||
Fraction of Daily Salary for Half Day,Bruchteil des Tagesgehalts für einen halben Tag,
|
||||
@@ -9116,12 +9130,12 @@ Show 'Scan Barcode' field above every child table to insert Items with ease.,"Ze
|
||||
"If blank, parent Warehouse Account or company default will be considered in transactions","Wenn leer, wird das übergeordnete Lagerkonto oder der Firmenstandard bei Transaktionen berücksichtigt",
|
||||
Service Level Agreement Details,Details zum Service Level Agreement,
|
||||
Service Level Agreement Status,Status des Service Level Agreements,
|
||||
On Hold Since,In der Warteschleife seit,
|
||||
On Hold Since,Auf Eis gelegt seit,
|
||||
Total Hold Time,Gesamte Haltezeit,
|
||||
Response Details,Antwortdetails,
|
||||
Average Response Time,Durchschnittliche Reaktionszeit,
|
||||
User Resolution Time,Benutzerauflösungszeit,
|
||||
SLA is on hold since {0},"SLA wird gehalten, da {0}",
|
||||
SLA is on hold since {0},"SLA ist seit {0} auf Eis gelegt",
|
||||
Pause SLA On Status,SLA On Status anhalten,
|
||||
Pause SLA On,SLA anhalten Ein,
|
||||
Greetings Section,Grüße Abschnitt,
|
||||
@@ -9335,6 +9349,7 @@ Total Assets,Gesamtvermögen,
|
||||
New Assets (This Year),Neue Vermögenswerte (dieses Jahr),
|
||||
Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.,Zeile # {}: Das Buchungsdatum der Abschreibung sollte nicht dem Datum der Verfügbarkeit entsprechen.,
|
||||
Incorrect Date,Falsches Datum,
|
||||
Incorrect Payment Type,Falsche Zahlungsart,
|
||||
Invalid Gross Purchase Amount,Ungültiger Bruttokaufbetrag,
|
||||
There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.,"Es gibt aktive Wartungs- oder Reparaturarbeiten am Vermögenswert. Sie müssen alle Schritte ausführen, bevor Sie das Asset stornieren können.",
|
||||
% Complete,% Komplett,
|
||||
@@ -9357,7 +9372,7 @@ Please check your Plaid client ID and secret values,Bitte überprüfen Sie Ihre
|
||||
Bank transaction creation error,Fehler beim Erstellen der Banküberweisung,
|
||||
Unit of Measurement,Maßeinheit,
|
||||
Fiscal Year {0} Does Not Exist,Geschäftsjahr {0} existiert nicht,
|
||||
Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile # {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden,
|
||||
Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden,
|
||||
Valuation type charges can not be marked as Inclusive,Bewertungsgebühren können nicht als Inklusiv gekennzeichnet werden,
|
||||
You do not have permissions to {} items in a {}.,Sie haben keine Berechtigungen für {} Elemente in einem {}.,
|
||||
Insufficient Permissions,Nicht ausreichende Berechtigungen,
|
||||
@@ -9368,7 +9383,7 @@ Invalid Value,Ungültiger Wert,
|
||||
The value {0} is already assigned to an existing Item {1}.,Der Wert {0} ist bereits einem vorhandenen Element {1} zugeordnet.,
|
||||
"To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.","Aktivieren Sie {0} in den Einstellungen für Elementvarianten, um mit der Bearbeitung dieses Attributwerts fortzufahren.",
|
||||
Edit Not Allowed,Bearbeiten nicht erlaubt,
|
||||
Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile # {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen.,
|
||||
Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen.,
|
||||
You cannot create or cancel any accounting entries with in the closed Accounting Period {0},Sie können im abgeschlossenen Abrechnungszeitraum {0} keine Buchhaltungseinträge mit erstellen oder stornieren.,
|
||||
POS Invoice should have {} field checked.,Für die POS-Rechnung sollte das Feld {} aktiviert sein.,
|
||||
Invalid Item,Ungültiger Artikel,
|
||||
@@ -9470,7 +9485,7 @@ Only submittted unpledge requests can be approved,Es können nur übermittelte n
|
||||
Interest Amount or Principal Amount is mandatory,Der Zinsbetrag oder der Kapitalbetrag ist obligatorisch,
|
||||
Disbursed Amount cannot be greater than {0},Der ausgezahlte Betrag darf nicht größer als {0} sein.,
|
||||
Row {0}: Loan Security {1} added multiple times,Zeile {0}: Darlehenssicherheit {1} wurde mehrmals hinzugefügt,
|
||||
Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile # {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie,
|
||||
Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie,
|
||||
Credit limit reached for customer {0},Kreditlimit für Kunde erreicht {0},
|
||||
Could not auto create Customer due to the following missing mandatory field(s):,Der Kunde konnte aufgrund der folgenden fehlenden Pflichtfelder nicht automatisch erstellt werden:,
|
||||
Please create Customer from Lead {0}.,Bitte erstellen Sie einen Kunden aus Lead {0}.,
|
||||
@@ -9482,7 +9497,7 @@ Payroll date can not be less than employee's joining date.,Das Abrechnungsdatum
|
||||
From date can not be less than employee's joining date.,Ab dem Datum darf das Beitrittsdatum des Mitarbeiters nicht unterschritten werden.,
|
||||
To date can not be greater than employee's relieving date.,Bisher kann das Entlastungsdatum des Mitarbeiters nicht überschritten werden.,
|
||||
Payroll date can not be greater than employee's relieving date.,Das Abrechnungsdatum darf nicht größer sein als das Entlastungsdatum des Mitarbeiters.,
|
||||
Row #{0}: Please enter the result value for {1},Zeile # {0}: Bitte geben Sie den Ergebniswert für {1} ein,
|
||||
Row #{0}: Please enter the result value for {1},Zeile {0}: Bitte geben Sie den Ergebniswert für {1} ein,
|
||||
Mandatory Results,Obligatorische Ergebnisse,
|
||||
Sales Invoice or Patient Encounter is required to create Lab Tests,Für die Erstellung von Labortests ist eine Verkaufsrechnung oder eine Patientenbegegnung erforderlich,
|
||||
Insufficient Data,Unzureichende Daten,
|
||||
@@ -9490,12 +9505,12 @@ Lab Test(s) {0} created successfully,Labortest (e) {0} erfolgreich erstellt,
|
||||
Test :,Prüfung :,
|
||||
Sample Collection {0} has been created,Die Probensammlung {0} wurde erstellt,
|
||||
Normal Range: ,Normalbereich:,
|
||||
Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile # {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein,
|
||||
Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein,
|
||||
"Missing required details, did not create Inpatient Record","Fehlende erforderliche Details, keine stationäre Aufzeichnung erstellt",
|
||||
Unbilled Invoices,Nicht in Rechnung gestellte Rechnungen,
|
||||
Standard Selling Rate should be greater than zero.,Die Standardverkaufsrate sollte größer als Null sein.,
|
||||
Conversion Factor is mandatory,Der Umrechnungsfaktor ist obligatorisch,
|
||||
Row #{0}: Conversion Factor is mandatory,Zeile # {0}: Der Umrechnungsfaktor ist obligatorisch,
|
||||
Row #{0}: Conversion Factor is mandatory,Zeile {0}: Der Umrechnungsfaktor ist obligatorisch,
|
||||
Sample Quantity cannot be negative or 0,Die Probenmenge darf nicht negativ oder 0 sein,
|
||||
Invalid Quantity,Ungültige Menge,
|
||||
"Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings","Bitte legen Sie in den Verkaufseinstellungen die Standardeinstellungen für Kundengruppe, Gebiet und Verkaufspreisliste fest",
|
||||
@@ -9687,7 +9702,7 @@ Row #{}: No batch selected against item: {}. Please select a batch or remove it
|
||||
Payment amount cannot be less than or equal to 0,Der Zahlungsbetrag darf nicht kleiner oder gleich 0 sein,
|
||||
Please enter the phone number first,Bitte geben Sie zuerst die Telefonnummer ein,
|
||||
Row #{}: {} {} does not exist.,Zeile # {}: {} {} existiert nicht.,
|
||||
Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile # {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen",
|
||||
Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen",
|
||||
You had {} errors while creating opening invoices. Check {} for more details,Beim Erstellen von Eröffnungsrechnungen sind {} Fehler aufgetreten. Überprüfen Sie {} auf weitere Details,
|
||||
Error Occured,Fehler aufgetreten,
|
||||
Opening Invoice Creation In Progress,Öffnen der Rechnungserstellung läuft,
|
||||
@@ -9699,7 +9714,7 @@ Stock Transactions for Item {0} under warehouse {1} cannot be posted before this
|
||||
Posting future stock transactions are not allowed due to Immutable Ledger,Das Buchen zukünftiger Lagertransaktionen ist aufgrund des unveränderlichen Hauptbuchs nicht zulässig,
|
||||
A BOM with name {0} already exists for item {1}.,Für Artikel {1} ist bereits eine Stückliste mit dem Namen {0} vorhanden.,
|
||||
{0}{1} Did you rename the item? Please contact Administrator / Tech support,{0} {1} Haben Sie den Artikel umbenannt? Bitte wenden Sie sich an den Administrator / technischen Support,
|
||||
At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile # {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}.,
|
||||
At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}.,
|
||||
The {0} ({1}) must be equal to {2} ({3}),Die {0} ({1}) muss gleich {2} ({3}) sein.,
|
||||
"{0}, complete the operation {1} before the operation {2}.","{0}, schließen Sie die Operation {1} vor der Operation {2} ab.",
|
||||
Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No.,"Die Lieferung per Seriennummer kann nicht sichergestellt werden, da Artikel {0} mit und ohne Lieferung per Seriennummer hinzugefügt wird.",
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -4,7 +4,7 @@ googlemaps # used in ERPNext, but dependency is defined in Frappe
|
||||
pandas>=1.1.5,<2.0.0
|
||||
plaid-python~=7.2.1
|
||||
pycountry~=20.7.3
|
||||
PyGithub~=1.54.1
|
||||
PyGithub~=2.1.1
|
||||
python-stdnum~=1.16
|
||||
python-youtube~=0.8.0
|
||||
taxjar~=1.9.2
|
||||
|
||||
Reference in New Issue
Block a user