mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-12 19:35:09 +00:00
Merge pull request #38086 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -112,7 +112,8 @@ class AutoMatchbyPartyNameDescription:
|
||||
|
||||
for party in parties:
|
||||
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
|
||||
names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name")
|
||||
field = party.lower() + "_name"
|
||||
names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"])
|
||||
|
||||
for field in ["bank_party_name", "description"]:
|
||||
if not self.get(field):
|
||||
@@ -131,7 +132,11 @@ class AutoMatchbyPartyNameDescription:
|
||||
|
||||
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
|
||||
skip = False
|
||||
result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio)
|
||||
result = process.extract(
|
||||
query=self.get(field),
|
||||
choices={row.get("name"): row.get("party_name") for row in names},
|
||||
scorer=fuzz.token_set_ratio,
|
||||
)
|
||||
party_name, skip = self.process_fuzzy_result(result)
|
||||
|
||||
if not party_name:
|
||||
@@ -149,14 +154,14 @@ class AutoMatchbyPartyNameDescription:
|
||||
|
||||
Returns: Result, Skip (whether or not to discontinue matching)
|
||||
"""
|
||||
PARTY, SCORE, CUTOFF = 0, 1, 80
|
||||
SCORE, PARTY_ID, CUTOFF = 1, 2, 80
|
||||
|
||||
if not result or not len(result):
|
||||
return None, False
|
||||
|
||||
first_result = result[0]
|
||||
if len(result) == 1:
|
||||
return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True
|
||||
return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True
|
||||
|
||||
second_result = result[1]
|
||||
if first_result[SCORE] > CUTOFF:
|
||||
@@ -165,7 +170,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
if first_result[SCORE] == second_result[SCORE]:
|
||||
return None, True
|
||||
|
||||
return first_result[PARTY], True
|
||||
return first_result[PARTY_ID], True
|
||||
else:
|
||||
return None, False
|
||||
|
||||
|
||||
@@ -53,10 +53,18 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label : "Company",
|
||||
fieldname: "company",
|
||||
fieldtype: "Link",
|
||||
reqd: 1,
|
||||
hidden: 1,
|
||||
default: frm.doc.company,
|
||||
},
|
||||
],
|
||||
primary_action: function() {
|
||||
var data = d.get_values();
|
||||
let data = d.get_values();
|
||||
|
||||
if (!data.template_type) {
|
||||
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
||||
@@ -66,7 +74,8 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
||||
{
|
||||
file_type: data.file_type,
|
||||
template_type: data.template_type
|
||||
template_type: data.template_type,
|
||||
company: data.company
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from functools import reduce
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.form.linked_with import get_linked_fields
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, cstr
|
||||
from frappe.utils.csvutils import UnicodeWriter
|
||||
@@ -294,10 +295,8 @@ def build_response_as_excel(writer):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_template(file_type, template_type):
|
||||
data = frappe._dict(frappe.local.form_dict)
|
||||
|
||||
writer = get_template(template_type)
|
||||
def download_template(file_type, template_type, company):
|
||||
writer = get_template(template_type, company)
|
||||
|
||||
if file_type == "CSV":
|
||||
# download csv file
|
||||
@@ -308,8 +307,7 @@ def download_template(file_type, template_type):
|
||||
build_response_as_excel(writer)
|
||||
|
||||
|
||||
def get_template(template_type):
|
||||
|
||||
def get_template(template_type, company):
|
||||
fields = [
|
||||
"Account Name",
|
||||
"Parent Account",
|
||||
@@ -335,34 +333,17 @@ def get_template(template_type):
|
||||
["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
|
||||
)
|
||||
else:
|
||||
writer = get_sample_template(writer)
|
||||
writer = get_sample_template(writer, company)
|
||||
|
||||
return writer
|
||||
|
||||
|
||||
def get_sample_template(writer):
|
||||
template = [
|
||||
["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
|
||||
["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"],
|
||||
["Equity", "", "", "", 1, "", "Equity"],
|
||||
["Expenses", "", "", "", 1, "", "Expense"],
|
||||
["Income", "", "", "", 1, "", "Income"],
|
||||
["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"],
|
||||
["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"],
|
||||
["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"],
|
||||
["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"],
|
||||
["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"],
|
||||
["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"],
|
||||
["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"],
|
||||
["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"],
|
||||
["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"],
|
||||
["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"],
|
||||
["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"],
|
||||
["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"],
|
||||
]
|
||||
|
||||
for row in template:
|
||||
writer.writerow(row)
|
||||
def get_sample_template(writer, company):
|
||||
currency = frappe.db.get_value("Company", company, "default_currency")
|
||||
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
|
||||
for row in f:
|
||||
row = row.strip().split(",") + [currency]
|
||||
writer.writerow(row)
|
||||
|
||||
return writer
|
||||
|
||||
@@ -453,14 +434,11 @@ def get_mandatory_account_types():
|
||||
|
||||
|
||||
def unset_existing_data(company):
|
||||
linked = frappe.db.sql(
|
||||
'''select fieldname from tabDocField
|
||||
where fieldtype="Link" and options="Account" and parent="Company"''',
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
# remove accounts data from company
|
||||
update_values = {d.fieldname: "" for d in linked}
|
||||
|
||||
fieldnames = get_linked_fields("Account").get("Company", {}).get("fieldname", [])
|
||||
linked = [{"fieldname": name} for name in fieldnames]
|
||||
update_values = {d.get("fieldname"): "" for d in linked}
|
||||
frappe.db.set_value("Company", company, update_values, update_values)
|
||||
|
||||
# remove accounts data from various doctypes
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Application Of Funds(Assets),,,,1,,Asset
|
||||
Sources Of Funds(Liabilities),,,,1,,Liability
|
||||
Equity,,,,1,,Equity
|
||||
Expenses,,,,1,Expense Account,Expense
|
||||
Income,,,,1,Income Account,Income
|
||||
Bank Accounts,Application Of Funds(Assets),,,1,Bank,Asset
|
||||
Cash In Hand,Application Of Funds(Assets),,,1,Cash,Asset
|
||||
Stock Assets,Application Of Funds(Assets),,,1,Stock,Asset
|
||||
Cost Of Goods Sold,Expenses,,,0,Cost of Goods Sold,Expense
|
||||
Asset Depreciation,Expenses,,,0,Depreciation,Expense
|
||||
Fixed Assets,Application Of Funds(Assets),,,0,Fixed Asset,Asset
|
||||
Accounts Payable,Sources Of Funds(Liabilities),,,0,Payable,Liability
|
||||
Accounts Receivable,Application Of Funds(Assets),,,1,Receivable,Asset
|
||||
Stock Expenses,Expenses,,,0,Stock Adjustment,Expense
|
||||
Sample Bank,Bank Accounts,,,0,Bank,Asset
|
||||
Cash,Cash In Hand,,,0,Cash,Asset
|
||||
Stores,Stock Assets,,,0,Stock,Asset
|
||||
|
@@ -114,10 +114,13 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_group",
|
||||
"fieldname":"customer_group",
|
||||
"label": __("Customer Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer Group"
|
||||
"fieldtype": "MultiSelectList",
|
||||
"options": "Customer Group",
|
||||
get_data: function(txt) {
|
||||
return frappe.db.get_link_options('Customer Group', txt);
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_terms_template",
|
||||
|
||||
@@ -849,7 +849,13 @@ class ReceivablePayableReport(object):
|
||||
self.customer = qb.DocType("Customer")
|
||||
|
||||
if self.filters.get("customer_group"):
|
||||
self.get_hierarchical_filters("Customer Group", "customer_group")
|
||||
groups = get_customer_group_with_children(self.filters.customer_group)
|
||||
customers = (
|
||||
qb.from_(self.customer)
|
||||
.select(self.customer.name)
|
||||
.where(self.customer["customer_group"].isin(groups))
|
||||
)
|
||||
self.qb_selection_filter.append(self.ple.party.isin(customers))
|
||||
|
||||
if self.filters.get("territory"):
|
||||
self.get_hierarchical_filters("Territory", "territory")
|
||||
@@ -1141,3 +1147,19 @@ class ReceivablePayableReport(object):
|
||||
.run()
|
||||
)
|
||||
self.err_journals = [x[0] for x in results] if results else []
|
||||
|
||||
|
||||
def get_customer_group_with_children(customer_groups):
|
||||
if not isinstance(customer_groups, list):
|
||||
customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d]
|
||||
|
||||
all_customer_groups = []
|
||||
for d in customer_groups:
|
||||
if frappe.db.exists("Customer Group", d):
|
||||
lft, rgt = frappe.db.get_value("Customer Group", d, ["lft", "rgt"])
|
||||
children = frappe.get_all("Customer Group", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
|
||||
all_customer_groups += [c.name for c in children]
|
||||
else:
|
||||
frappe.throw(_("Customer Group: {0} does not exist").format(d))
|
||||
|
||||
return list(set(all_customer_groups))
|
||||
|
||||
@@ -475,6 +475,30 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
||||
report = execute(filters)[1]
|
||||
self.assertEqual(len(report), 0)
|
||||
|
||||
def test_multi_customer_group_filter(self):
|
||||
si = self.create_sales_invoice()
|
||||
cus_group = frappe.db.get_value("Customer", self.customer, "customer_group")
|
||||
# Create a list of customer groups, e.g., ["Group1", "Group2"]
|
||||
cus_groups_list = [cus_group, "_Test Customer Group 1"]
|
||||
|
||||
filters = {
|
||||
"company": self.company,
|
||||
"report_date": today(),
|
||||
"range1": 30,
|
||||
"range2": 60,
|
||||
"range3": 90,
|
||||
"range4": 120,
|
||||
"customer_group": cus_groups_list, # Use the list of customer groups
|
||||
}
|
||||
report = execute(filters)[1]
|
||||
|
||||
# Assert that the report contains data for the specified customer groups
|
||||
self.assertTrue(len(report) > 0)
|
||||
|
||||
for row in report:
|
||||
# Assert that the customer group of each row is in the list of customer groups
|
||||
self.assertIn(row.customer_group, cus_groups_list)
|
||||
|
||||
def test_party_account_filter(self):
|
||||
si1 = self.create_sales_invoice()
|
||||
self.customer2 = (
|
||||
|
||||
@@ -338,7 +338,7 @@ class BuyingController(SubcontractingController):
|
||||
{
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.get("from_warehouse"),
|
||||
"posting_date": self.get("posting_date") or self.get("transation_date"),
|
||||
"posting_date": self.get("posting_date") or self.get("transaction_date"),
|
||||
"posting_time": posting_time,
|
||||
"qty": -1 * flt(d.get("stock_qty")),
|
||||
"serial_no": d.get("serial_no"),
|
||||
|
||||
@@ -89,10 +89,6 @@ frappe.ui.form.on('Production Plan', {
|
||||
frm.trigger("show_progress");
|
||||
|
||||
if (frm.doc.status !== "Completed") {
|
||||
frm.add_custom_button(__("Work Order Tree"), ()=> {
|
||||
frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name});
|
||||
}, __('View'));
|
||||
|
||||
frm.add_custom_button(__("Production Plan Summary"), ()=> {
|
||||
frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
|
||||
}, __('View'));
|
||||
|
||||
@@ -827,8 +827,6 @@ class ProductionPlan(Document):
|
||||
# Combine subassembly items
|
||||
sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
|
||||
|
||||
sub_assembly_items_store.sort(key=lambda d: d.bom_level, reverse=True) # sort by bom level
|
||||
|
||||
for idx, row in enumerate(sub_assembly_items_store):
|
||||
row.idx = idx + 1
|
||||
self.append("sub_assembly_items", row)
|
||||
|
||||
@@ -653,49 +653,6 @@ class TestProductionPlan(FrappeTestCase):
|
||||
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_subassmebly_sorting(self):
|
||||
"Test subassembly sorting in case of multiple items with nested BOMs."
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
|
||||
|
||||
prefix = "_TestLevel_"
|
||||
boms = {
|
||||
"Assembly": {
|
||||
"SubAssembly1": {
|
||||
"ChildPart1": {},
|
||||
"ChildPart2": {},
|
||||
},
|
||||
"ChildPart6": {},
|
||||
"SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}},
|
||||
},
|
||||
"MegaDeepAssy": {
|
||||
"SecretSubassy": {
|
||||
"SecretPart": {"VerySecret": {"SuperSecret": {"Classified": {}}}},
|
||||
},
|
||||
# ^ assert that this is
|
||||
# first item in subassy table
|
||||
},
|
||||
}
|
||||
create_nested_bom(boms, prefix=prefix)
|
||||
|
||||
items = [prefix + item_code for item_code in boms.keys()]
|
||||
plan = create_production_plan(item_code=items[0], do_not_save=True)
|
||||
plan.append(
|
||||
"po_items",
|
||||
{
|
||||
"use_multi_level_bom": 1,
|
||||
"item_code": items[1],
|
||||
"bom_no": frappe.db.get_value("Item", items[1], "default_bom"),
|
||||
"planned_qty": 1,
|
||||
"planned_start_date": now_datetime(),
|
||||
},
|
||||
)
|
||||
plan.get_sub_assembly_items()
|
||||
|
||||
bom_level_order = [d.bom_level for d in plan.sub_assembly_items]
|
||||
self.assertEqual(bom_level_order, sorted(bom_level_order, reverse=True))
|
||||
# lowest most level of subassembly should be first
|
||||
self.assertIn("SuperSecret", plan.sub_assembly_items[0].production_item)
|
||||
|
||||
def test_multiple_work_order_for_production_plan_item(self):
|
||||
"Test producing Prod Plan (making WO) in parts."
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ frappe.query_reports["Production Plan Summary"] = {
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (column.fieldname == "document_name") {
|
||||
if (column.fieldname == "item_code") {
|
||||
var color = data.pending_qty > 0 ? 'red': 'green';
|
||||
value = `<a style='color:${color}' href="#Form/${data['document_type']}/${data['document_name']}" data-doctype="${data['document_type']}">${data['document_name']}</a>`;
|
||||
value = `<a style='color:${color}' href="/app/item/${data['item_code']}" data-doctype="Item">${data['item_code']}</a>`;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -44,6 +44,7 @@ def get_production_plan_item_details(filters, data, order_details):
|
||||
{
|
||||
"indent": 0,
|
||||
"item_code": row.item_code,
|
||||
"sales_order": row.get("sales_order"),
|
||||
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
|
||||
"qty": row.planned_qty,
|
||||
"document_type": "Work Order",
|
||||
@@ -80,7 +81,7 @@ def get_production_plan_sub_assembly_item_details(
|
||||
|
||||
data.append(
|
||||
{
|
||||
"indent": 1,
|
||||
"indent": 1 + item.indent,
|
||||
"item_code": item.production_item,
|
||||
"item_name": item.item_name,
|
||||
"qty": item.qty,
|
||||
@@ -98,7 +99,7 @@ def get_work_order_details(filters, order_details):
|
||||
for row in frappe.get_all(
|
||||
"Work Order",
|
||||
filters={"production_plan": filters.get("production_plan")},
|
||||
fields=["name", "produced_qty", "production_plan", "production_item"],
|
||||
fields=["name", "produced_qty", "production_plan", "production_item", "sales_order"],
|
||||
):
|
||||
order_details.setdefault((row.name, row.production_item), row)
|
||||
|
||||
@@ -118,10 +119,17 @@ def get_column(filters):
|
||||
"label": _("Finished Good"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "item_code",
|
||||
"width": 300,
|
||||
"width": 240,
|
||||
"options": "Item",
|
||||
},
|
||||
{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
|
||||
{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 150},
|
||||
{
|
||||
"label": _("Sales Order"),
|
||||
"options": "Sales Order",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "sales_order",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Document Type"),
|
||||
"fieldtype": "Link",
|
||||
@@ -133,10 +141,16 @@ def get_column(filters):
|
||||
"label": _("Document Name"),
|
||||
"fieldtype": "Dynamic Link",
|
||||
"fieldname": "document_name",
|
||||
"width": 150,
|
||||
"options": "document_type",
|
||||
"width": 180,
|
||||
},
|
||||
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
|
||||
{"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120},
|
||||
{"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160},
|
||||
{
|
||||
"label": _("Produced / Received Qty"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "produced_qty",
|
||||
"width": 200,
|
||||
},
|
||||
{"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
|
||||
]
|
||||
|
||||
@@ -541,6 +541,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
||||
primary_action={
|
||||
"label": "Send Email",
|
||||
"server_action": "erpnext.selling.doctype.customer.customer.send_emails",
|
||||
"hide_on_success": True,
|
||||
"args": {
|
||||
"customer": customer,
|
||||
"customer_outstanding": customer_outstanding,
|
||||
|
||||
@@ -80,7 +80,7 @@ def get_data(filters=None):
|
||||
|
||||
territory_orders = []
|
||||
if t_quotation_names and sales_orders:
|
||||
list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
|
||||
territory_orders = list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
|
||||
t_order_names = []
|
||||
if territory_orders:
|
||||
t_order_names = [t.name for t in territory_orders]
|
||||
|
||||
@@ -108,7 +108,16 @@ class TransactionDeletionRecord(Document):
|
||||
|
||||
if no_of_docs > 0:
|
||||
self.delete_version_log(docfield["parent"], docfield["fieldname"])
|
||||
self.delete_communications(docfield["parent"], docfield["fieldname"])
|
||||
|
||||
reference_docs = frappe.get_all(
|
||||
docfield["parent"], filters={docfield["fieldname"]: self.company}
|
||||
)
|
||||
reference_doc_names = [r.name for r in reference_docs]
|
||||
|
||||
self.delete_communications(docfield["parent"], reference_doc_names)
|
||||
self.delete_comments(docfield["parent"], reference_doc_names)
|
||||
self.unlink_attachments(docfield["parent"], reference_doc_names)
|
||||
|
||||
self.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
|
||||
|
||||
self.delete_child_tables(docfield["parent"], docfield["fieldname"])
|
||||
@@ -197,19 +206,49 @@ class TransactionDeletionRecord(Document):
|
||||
(versions.ref_doctype == doctype) & (versions.docname.isin(batch))
|
||||
).run()
|
||||
|
||||
def delete_communications(self, doctype, company_fieldname):
|
||||
reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
|
||||
reference_doc_names = [r.name for r in reference_docs]
|
||||
|
||||
def delete_communications(self, doctype, reference_doc_names):
|
||||
communications = frappe.get_all(
|
||||
"Communication",
|
||||
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
|
||||
)
|
||||
communication_names = [c.name for c in communications]
|
||||
|
||||
if not communication_names:
|
||||
return
|
||||
|
||||
for batch in create_batch(communication_names, self.batch_size):
|
||||
frappe.delete_doc("Communication", batch, ignore_permissions=True)
|
||||
|
||||
def delete_comments(self, doctype, reference_doc_names):
|
||||
comments = frappe.get_all(
|
||||
"Comment",
|
||||
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
|
||||
)
|
||||
comment_names = [c.name for c in comments]
|
||||
|
||||
if not comment_names:
|
||||
return
|
||||
|
||||
for batch in create_batch(comment_names, self.batch_size):
|
||||
frappe.delete_doc("Comment", batch, ignore_permissions=True)
|
||||
|
||||
def unlink_attachments(self, doctype, reference_doc_names):
|
||||
files = frappe.get_all(
|
||||
"File",
|
||||
filters={"attached_to_doctype": doctype, "attached_to_name": ["in", reference_doc_names]},
|
||||
)
|
||||
file_names = [c.name for c in files]
|
||||
|
||||
if not file_names:
|
||||
return
|
||||
|
||||
file = qb.DocType("File")
|
||||
|
||||
for batch in create_batch(file_names, self.batch_size):
|
||||
qb.update(file).set(file.attached_to_doctype, None).set(file.attached_to_name, None).where(
|
||||
file.name.isin(batch)
|
||||
).run()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_doctypes_to_be_ignored():
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"oldfieldname": "item",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Item",
|
||||
"read_only_depends_on": "eval:!doc.__islocal",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -207,7 +208,7 @@
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"max_attachments": 5,
|
||||
"modified": "2022-02-21 08:08:23.999236",
|
||||
"modified": "2023-11-09 12:17:28.339975",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Batch",
|
||||
|
||||
@@ -3671,7 +3671,7 @@ Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert
|
||||
Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.",
|
||||
Cannot create loan until application is approved,"Darlehen kann erst erstellt werden, wenn der Antrag genehmigt wurde",
|
||||
Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen.,
|
||||
"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Kontoeinstellungen fest",
|
||||
"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Für Artikel {0} in Zeile {1} kann nicht mehr als {2} zusätzlich in Rechnung gestellt werden. Um diese Überfakturierung zuzulassen, passen Sie bitte die Grenzwerte in den Buchhaltungseinstellungen an.",
|
||||
"Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen",
|
||||
Categories,Kategorien,
|
||||
Changes in {0},Änderungen in {0},
|
||||
@@ -4125,7 +4125,7 @@ This page keeps track of your items in which buyers have showed some interest.,"
|
||||
Thursday,Donnerstag,
|
||||
Timing,Zeitliche Koordinierung,
|
||||
Title,Bezeichnung,
|
||||
"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Kontoeinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
|
||||
"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Buchhaltungseinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
|
||||
"To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.",
|
||||
To date needs to be before from date,Das Datum muss vor dem Datum liegen,
|
||||
Total,Summe,
|
||||
@@ -4533,8 +4533,8 @@ Mandatory For Profit and Loss Account,Obligatorisch für Gewinn- und Verlustrech
|
||||
Accounting Period,Abrechnungszeitraum,
|
||||
Period Name,Zeitraumname,
|
||||
Closed Documents,Geschlossene Dokumente,
|
||||
Accounts Settings,Konteneinstellungen,
|
||||
Settings for Accounts,Konteneinstellungen,
|
||||
Accounts Settings,Buchhaltungseinstellungen,
|
||||
Settings for Accounts,Einstellungen für die Buchhaltung,
|
||||
Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen,
|
||||
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern,
|
||||
Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
|
||||
@@ -8622,7 +8622,7 @@ Template Title,Vorlagentitel,
|
||||
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,
|
||||
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 Buchhaltungseinstellungen und versuchen Sie es erneut,
|
||||
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
|
||||
Total Counts Targeted,Gesamtzahl der anvisierten Zählungen,
|
||||
Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen,
|
||||
|
||||
|
Can't render this file because it is too large.
|
Reference in New Issue
Block a user