Merge pull request #38086 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
Deepesh Garg
2023-11-14 18:25:20 +05:30
committed by GitHub
18 changed files with 183 additions and 119 deletions

View File

@@ -112,7 +112,8 @@ class AutoMatchbyPartyNameDescription:
for party in parties: for party in parties:
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0} 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"]: for field in ["bank_party_name", "description"]:
if not self.get(field): if not self.get(field):
@@ -131,7 +132,11 @@ class AutoMatchbyPartyNameDescription:
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]: def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
skip = False 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) party_name, skip = self.process_fuzzy_result(result)
if not party_name: if not party_name:
@@ -149,14 +154,14 @@ class AutoMatchbyPartyNameDescription:
Returns: Result, Skip (whether or not to discontinue matching) 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): if not result or not len(result):
return None, False return None, False
first_result = result[0] first_result = result[0]
if len(result) == 1: 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] second_result = result[1]
if first_result[SCORE] > CUTOFF: if first_result[SCORE] > CUTOFF:
@@ -165,7 +170,7 @@ class AutoMatchbyPartyNameDescription:
if first_result[SCORE] == second_result[SCORE]: if first_result[SCORE] == second_result[SCORE]:
return None, True return None, True
return first_result[PARTY], True return first_result[PARTY_ID], True
else: else:
return None, False return None, False

View File

@@ -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.`); 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() { primary_action: function() {
var data = d.get_values(); let data = d.get_values();
if (!data.template_type) { if (!data.template_type) {
frappe.throw(__('Please select <b>Template Type</b> to download template')); 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', '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
{ {
file_type: data.file_type, file_type: data.file_type,
template_type: data.template_type template_type: data.template_type,
company: data.company
} }
); );

View File

@@ -8,6 +8,7 @@ from functools import reduce
import frappe import frappe
from frappe import _ from frappe import _
from frappe.desk.form.linked_with import get_linked_fields
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cint, cstr from frappe.utils import cint, cstr
from frappe.utils.csvutils import UnicodeWriter from frappe.utils.csvutils import UnicodeWriter
@@ -294,10 +295,8 @@ def build_response_as_excel(writer):
@frappe.whitelist() @frappe.whitelist()
def download_template(file_type, template_type): def download_template(file_type, template_type, company):
data = frappe._dict(frappe.local.form_dict) writer = get_template(template_type, company)
writer = get_template(template_type)
if file_type == "CSV": if file_type == "CSV":
# download csv file # download csv file
@@ -308,8 +307,7 @@ def download_template(file_type, template_type):
build_response_as_excel(writer) build_response_as_excel(writer)
def get_template(template_type): def get_template(template_type, company):
fields = [ fields = [
"Account Name", "Account Name",
"Parent Account", "Parent Account",
@@ -335,34 +333,17 @@ def get_template(template_type):
["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")] ["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
) )
else: else:
writer = get_sample_template(writer) writer = get_sample_template(writer, company)
return writer return writer
def get_sample_template(writer): def get_sample_template(writer, company):
template = [ currency = frappe.db.get_value("Company", company, "default_currency")
["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"], with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"], for row in f:
["Equity", "", "", "", 1, "", "Equity"], row = row.strip().split(",") + [currency]
["Expenses", "", "", "", 1, "", "Expense"], writer.writerow(row)
["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)
return writer return writer
@@ -453,14 +434,11 @@ def get_mandatory_account_types():
def unset_existing_data(company): 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 # 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) frappe.db.set_value("Company", company, update_values, update_values)
# remove accounts data from various doctypes # remove accounts data from various doctypes

View File

@@ -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
1 Application Of Funds(Assets) 1 Asset
2 Sources Of Funds(Liabilities) 1 Liability
3 Equity 1 Equity
4 Expenses 1 Expense Account Expense
5 Income 1 Income Account Income
6 Bank Accounts Application Of Funds(Assets) 1 Bank Asset
7 Cash In Hand Application Of Funds(Assets) 1 Cash Asset
8 Stock Assets Application Of Funds(Assets) 1 Stock Asset
9 Cost Of Goods Sold Expenses 0 Cost of Goods Sold Expense
10 Asset Depreciation Expenses 0 Depreciation Expense
11 Fixed Assets Application Of Funds(Assets) 0 Fixed Asset Asset
12 Accounts Payable Sources Of Funds(Liabilities) 0 Payable Liability
13 Accounts Receivable Application Of Funds(Assets) 1 Receivable Asset
14 Stock Expenses Expenses 0 Stock Adjustment Expense
15 Sample Bank Bank Accounts 0 Bank Asset
16 Cash Cash In Hand 0 Cash Asset
17 Stores Stock Assets 0 Stock Asset

View File

@@ -114,10 +114,13 @@ frappe.query_reports["Accounts Receivable"] = {
"reqd": 1 "reqd": 1
}, },
{ {
"fieldname": "customer_group", "fieldname":"customer_group",
"label": __("Customer Group"), "label": __("Customer Group"),
"fieldtype": "Link", "fieldtype": "MultiSelectList",
"options": "Customer Group" "options": "Customer Group",
get_data: function(txt) {
return frappe.db.get_link_options('Customer Group', txt);
}
}, },
{ {
"fieldname": "payment_terms_template", "fieldname": "payment_terms_template",

View File

@@ -849,7 +849,13 @@ class ReceivablePayableReport(object):
self.customer = qb.DocType("Customer") self.customer = qb.DocType("Customer")
if self.filters.get("customer_group"): 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"): if self.filters.get("territory"):
self.get_hierarchical_filters("Territory", "territory") self.get_hierarchical_filters("Territory", "territory")
@@ -1141,3 +1147,19 @@ class ReceivablePayableReport(object):
.run() .run()
) )
self.err_journals = [x[0] for x in results] if results else [] 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))

View File

@@ -475,6 +475,30 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
report = execute(filters)[1] report = execute(filters)[1]
self.assertEqual(len(report), 0) 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): def test_party_account_filter(self):
si1 = self.create_sales_invoice() si1 = self.create_sales_invoice()
self.customer2 = ( self.customer2 = (

View File

@@ -338,7 +338,7 @@ class BuyingController(SubcontractingController):
{ {
"item_code": d.item_code, "item_code": d.item_code,
"warehouse": d.get("from_warehouse"), "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, "posting_time": posting_time,
"qty": -1 * flt(d.get("stock_qty")), "qty": -1 * flt(d.get("stock_qty")),
"serial_no": d.get("serial_no"), "serial_no": d.get("serial_no"),

View File

@@ -89,10 +89,6 @@ frappe.ui.form.on('Production Plan', {
frm.trigger("show_progress"); frm.trigger("show_progress");
if (frm.doc.status !== "Completed") { 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"), ()=> { frm.add_custom_button(__("Production Plan Summary"), ()=> {
frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name}); frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
}, __('View')); }, __('View'));

View File

@@ -827,8 +827,6 @@ class ProductionPlan(Document):
# Combine subassembly items # Combine subassembly items
sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store) 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): for idx, row in enumerate(sub_assembly_items_store):
row.idx = idx + 1 row.idx = idx + 1
self.append("sub_assembly_items", row) self.append("sub_assembly_items", row)

View File

@@ -653,49 +653,6 @@ class TestProductionPlan(FrappeTestCase):
frappe.db.rollback() 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): def test_multiple_work_order_for_production_plan_item(self):
"Test producing Prod Plan (making WO) in parts." "Test producing Prod Plan (making WO) in parts."

View File

@@ -22,9 +22,9 @@ frappe.query_reports["Production Plan Summary"] = {
"formatter": function(value, row, column, data, default_formatter) { "formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data); 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'; 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; return value;

View File

@@ -44,6 +44,7 @@ def get_production_plan_item_details(filters, data, order_details):
{ {
"indent": 0, "indent": 0,
"item_code": row.item_code, "item_code": row.item_code,
"sales_order": row.get("sales_order"),
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"), "item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
"qty": row.planned_qty, "qty": row.planned_qty,
"document_type": "Work Order", "document_type": "Work Order",
@@ -80,7 +81,7 @@ def get_production_plan_sub_assembly_item_details(
data.append( data.append(
{ {
"indent": 1, "indent": 1 + item.indent,
"item_code": item.production_item, "item_code": item.production_item,
"item_name": item.item_name, "item_name": item.item_name,
"qty": item.qty, "qty": item.qty,
@@ -98,7 +99,7 @@ def get_work_order_details(filters, order_details):
for row in frappe.get_all( for row in frappe.get_all(
"Work Order", "Work Order",
filters={"production_plan": filters.get("production_plan")}, 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) order_details.setdefault((row.name, row.production_item), row)
@@ -118,10 +119,17 @@ def get_column(filters):
"label": _("Finished Good"), "label": _("Finished Good"),
"fieldtype": "Link", "fieldtype": "Link",
"fieldname": "item_code", "fieldname": "item_code",
"width": 300, "width": 240,
"options": "Item", "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"), "label": _("Document Type"),
"fieldtype": "Link", "fieldtype": "Link",
@@ -133,10 +141,16 @@ def get_column(filters):
"label": _("Document Name"), "label": _("Document Name"),
"fieldtype": "Dynamic Link", "fieldtype": "Dynamic Link",
"fieldname": "document_name", "fieldname": "document_name",
"width": 150, "options": "document_type",
"width": 180,
}, },
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100}, {"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
{"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120}, {"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}, {"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
] ]

View File

@@ -541,6 +541,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
primary_action={ primary_action={
"label": "Send Email", "label": "Send Email",
"server_action": "erpnext.selling.doctype.customer.customer.send_emails", "server_action": "erpnext.selling.doctype.customer.customer.send_emails",
"hide_on_success": True,
"args": { "args": {
"customer": customer, "customer": customer,
"customer_outstanding": customer_outstanding, "customer_outstanding": customer_outstanding,

View File

@@ -80,7 +80,7 @@ def get_data(filters=None):
territory_orders = [] territory_orders = []
if t_quotation_names and sales_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 = [] t_order_names = []
if territory_orders: if territory_orders:
t_order_names = [t.name for t in territory_orders] t_order_names = [t.name for t in territory_orders]

View File

@@ -108,7 +108,16 @@ class TransactionDeletionRecord(Document):
if no_of_docs > 0: if no_of_docs > 0:
self.delete_version_log(docfield["parent"], docfield["fieldname"]) 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.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
self.delete_child_tables(docfield["parent"], docfield["fieldname"]) self.delete_child_tables(docfield["parent"], docfield["fieldname"])
@@ -197,19 +206,49 @@ class TransactionDeletionRecord(Document):
(versions.ref_doctype == doctype) & (versions.docname.isin(batch)) (versions.ref_doctype == doctype) & (versions.docname.isin(batch))
).run() ).run()
def delete_communications(self, doctype, company_fieldname): def delete_communications(self, doctype, reference_doc_names):
reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
reference_doc_names = [r.name for r in reference_docs]
communications = frappe.get_all( communications = frappe.get_all(
"Communication", "Communication",
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]}, filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
) )
communication_names = [c.name for c in communications] communication_names = [c.name for c in communications]
if not communication_names:
return
for batch in create_batch(communication_names, self.batch_size): for batch in create_batch(communication_names, self.batch_size):
frappe.delete_doc("Communication", batch, ignore_permissions=True) 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() @frappe.whitelist()
def get_doctypes_to_be_ignored(): def get_doctypes_to_be_ignored():

View File

@@ -61,6 +61,7 @@
"oldfieldname": "item", "oldfieldname": "item",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Item", "options": "Item",
"read_only_depends_on": "eval:!doc.__islocal",
"reqd": 1 "reqd": 1
}, },
{ {
@@ -207,7 +208,7 @@
"image_field": "image", "image_field": "image",
"links": [], "links": [],
"max_attachments": 5, "max_attachments": 5,
"modified": "2022-02-21 08:08:23.999236", "modified": "2023-11-09 12:17:28.339975",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Batch", "name": "Batch",

View File

@@ -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 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 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 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", "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, Categories,Kategorien,
Changes in {0},Änderungen in {0}, 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, Thursday,Donnerstag,
Timing,Zeitliche Koordinierung, Timing,Zeitliche Koordinierung,
Title,Bezeichnung, Title,Bezeichnung,
"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie &quot;Over Billing Allowance&quot; 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 &quot;Over Billing Allowance&quot; 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 &quot;Überbestätigung / Überlieferung&quot; in den Lagereinstellungen oder im Artikel.", "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 &quot;Überbestätigung / Überlieferung&quot; in den Lagereinstellungen oder im Artikel.",
To date needs to be before from date,Das Datum muss vor dem Datum liegen, To date needs to be before from date,Das Datum muss vor dem Datum liegen,
Total,Summe, Total,Summe,
@@ -4533,8 +4533,8 @@ Mandatory For Profit and Loss Account,Obligatorisch für Gewinn- und Verlustrech
Accounting Period,Abrechnungszeitraum, Accounting Period,Abrechnungszeitraum,
Period Name,Zeitraumname, Period Name,Zeitraumname,
Closed Documents,Geschlossene Dokumente, Closed Documents,Geschlossene Dokumente,
Accounts Settings,Konteneinstellungen, Accounts Settings,Buchhaltungseinstellungen,
Settings for Accounts,Konteneinstellungen, Settings for Accounts,Einstellungen für die Buchhaltung,
Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen, 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, 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, Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
@@ -8622,7 +8622,7 @@ Template Title,Vorlagentitel,
Journal Entry Type,Buchungssatz-Typ, Journal Entry Type,Buchungssatz-Typ,
Journal Entry Template Account,Buchungssatzvorlagenkonto, Journal Entry Template Account,Buchungssatzvorlagenkonto,
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten, 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, 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 Targeted,Gesamtzahl der anvisierten Zählungen,
Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen, Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen,
Can't render this file because it is too large.