Merge branch 'develop' into stock-reservation

This commit is contained in:
rohitwaghchaure 2023-05-24 14:32:04 +05:30 committed by GitHub
commit 565322daba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 930 additions and 192 deletions

View File

@ -904,7 +904,7 @@ frappe.ui.form.on('Payment Entry', {
function(d) { return flt(d.amount) })); function(d) { return flt(d.amount) }));
frm.set_value("difference_amount", difference_amount - total_deductions + frm.set_value("difference_amount", difference_amount - total_deductions +
frm.doc.base_total_taxes_and_charges); flt(frm.doc.base_total_taxes_and_charges));
frm.events.hide_unhide_fields(frm); frm.events.hide_unhide_fields(frm);
}, },

View File

@ -303,7 +303,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
apply_tds(frm) { apply_tds(frm) {
var me = this; var me = this;
me.frm.set_value("tax_withheld_vouchers", []);
if (!me.frm.doc.apply_tds) { if (!me.frm.doc.apply_tds) {
me.frm.set_value("tax_withholding_category", ''); me.frm.set_value("tax_withholding_category", '');
me.frm.set_df_property("tax_withholding_category", "hidden", 1); me.frm.set_df_property("tax_withholding_category", "hidden", 1);

View File

@ -1180,7 +1180,12 @@ class SalesInvoice(SellingController):
if self.is_return: if self.is_return:
fixed_asset_gl_entries = get_gl_entries_on_asset_regain( fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name") asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
) )
asset.db_set("disposal_date", None) asset.db_set("disposal_date", None)
@ -1208,7 +1213,12 @@ class SalesInvoice(SellingController):
asset.reload() asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal( fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name") asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
) )
asset.db_set("disposal_date", self.posting_date) asset.db_set("disposal_date", self.posting_date)

View File

@ -124,6 +124,7 @@
"fieldname": "rate", "fieldname": "rate",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Rate", "label": "Rate",
"options": "Company:company:default_currency",
"reqd": 1 "reqd": 1
}, },
{ {
@ -147,6 +148,7 @@
"fieldname": "amount", "fieldname": "amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Amount", "label": "Amount",
"options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
}, },
{ {

View File

@ -302,7 +302,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
"docstatus": 1, "docstatus": 1,
} }
if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice": if doctype != "Sales Invoice":
filters.update( filters.update(
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
) )

View File

@ -110,9 +110,9 @@ class TestTaxWithholdingCategory(unittest.TestCase):
invoices.append(pi1) invoices.append(pi1)
# Cumulative threshold is 30000 # Cumulative threshold is 30000
# Threshold calculation should be on both the invoices # Threshold calculation should be only on the Second invoice
# TDS should be applied only on 1000 # Second didn't breach, no TDS should be applied
self.assertEqual(pi1.taxes[0].tax_amount, 1000) self.assertEqual(pi1.taxes, [])
for d in reversed(invoices): for d in reversed(invoices):
d.cancel() d.cancel()

View File

@ -0,0 +1,41 @@
{
"creation": "2023-05-23 09:58:17.235916",
"docstatus": 0,
"doctype": "Form Tour",
"first_document": 0,
"idx": 0,
"include_name_field": 0,
"is_standard": 1,
"modified": "2023-05-23 13:10:56.227127",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"owner": "Administrator",
"reference_doctype": "Sales Invoice",
"save_on_complete": 1,
"steps": [
{
"description": "Select a customer for whom this invoice is being prepared.",
"fieldname": "customer",
"fieldtype": "Link",
"has_next_condition": 1,
"is_table_field": 0,
"label": "Customer",
"next_step_condition": "eval: doc.customer",
"position": "Right",
"title": "Select Customer"
},
{
"child_doctype": "Sales Invoice Item",
"description": "Select item that you have sold along with quantity and rate.",
"fieldname": "items",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"parent_fieldname": "items",
"position": "Top",
"title": "Select Item"
}
],
"title": "Sales Invoice"
}

View File

@ -19,14 +19,19 @@ def execute(filters=None):
return _execute(filters) return _execute(filters)
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): def _execute(
filters=None,
additional_table_columns=None,
additional_query_columns=None,
additional_conditions=None,
):
if not filters: if not filters:
filters = {} filters = {}
columns = get_columns(additional_table_columns, filters) columns = get_columns(additional_table_columns, filters)
company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
item_list = get_items(filters, additional_query_columns) item_list = get_items(filters, additional_query_columns, additional_conditions)
if item_list: if item_list:
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
@ -328,7 +333,7 @@ def get_columns(additional_table_columns, filters):
return columns return columns
def get_conditions(filters): def get_conditions(filters, additional_conditions=None):
conditions = "" conditions = ""
for opts in ( for opts in (
@ -341,6 +346,9 @@ def get_conditions(filters):
if filters.get(opts[0]): if filters.get(opts[0]):
conditions += opts[1] conditions += opts[1]
if additional_conditions:
conditions += additional_conditions
if filters.get("mode_of_payment"): if filters.get("mode_of_payment"):
conditions += """ and exists(select name from `tabSales Invoice Payment` conditions += """ and exists(select name from `tabSales Invoice Payment`
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
@ -376,8 +384,8 @@ def get_group_by_conditions(filters, doctype):
return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get("group_by"))) return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get("group_by")))
def get_items(filters, additional_query_columns): def get_items(filters, additional_query_columns, additional_conditions=None):
conditions = get_conditions(filters) conditions = get_conditions(filters, additional_conditions)
if additional_query_columns: if additional_query_columns:
additional_query_columns = ", " + ", ".join(additional_query_columns) additional_query_columns = ", " + ", ".join(additional_query_columns)

View File

@ -307,7 +307,7 @@ def scrap_asset(asset_name):
je.company = asset.company je.company = asset.company
je.remark = "Scrap Entry for asset {0}".format(asset_name) je.remark = "Scrap Entry for asset {0}".format(asset_name)
for entry in get_gl_entries_on_asset_disposal(asset): for entry in get_gl_entries_on_asset_disposal(asset, date):
entry.update({"reference_type": "Asset", "reference_name": asset_name}) entry.update({"reference_type": "Asset", "reference_name": asset_name})
je.append("accounts", entry) je.append("accounts", entry)
@ -434,8 +434,11 @@ def disposal_happens_in_the_future(posting_date_of_disposal):
def get_gl_entries_on_asset_regain( def get_gl_entries_on_asset_regain(
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None
): ):
if not date:
date = getdate()
( (
fixed_asset_account, fixed_asset_account,
asset, asset,
@ -453,7 +456,7 @@ def get_gl_entries_on_asset_regain(
"debit_in_account_currency": asset.gross_purchase_amount, "debit_in_account_currency": asset.gross_purchase_amount,
"debit": asset.gross_purchase_amount, "debit": asset.gross_purchase_amount,
"cost_center": depreciation_cost_center, "cost_center": depreciation_cost_center,
"posting_date": getdate(), "posting_date": date,
}, },
item=asset, item=asset,
), ),
@ -463,7 +466,7 @@ def get_gl_entries_on_asset_regain(
"credit_in_account_currency": accumulated_depr_amount, "credit_in_account_currency": accumulated_depr_amount,
"credit": accumulated_depr_amount, "credit": accumulated_depr_amount,
"cost_center": depreciation_cost_center, "cost_center": depreciation_cost_center,
"posting_date": getdate(), "posting_date": date,
}, },
item=asset, item=asset,
), ),
@ -472,7 +475,7 @@ def get_gl_entries_on_asset_regain(
profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount)) profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
if profit_amount: if profit_amount:
get_profit_gl_entries( get_profit_gl_entries(
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date
) )
if voucher_type and voucher_no: if voucher_type and voucher_no:
@ -484,8 +487,11 @@ def get_gl_entries_on_asset_regain(
def get_gl_entries_on_asset_disposal( def get_gl_entries_on_asset_disposal(
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None
): ):
if not date:
date = getdate()
( (
fixed_asset_account, fixed_asset_account,
asset, asset,
@ -503,7 +509,7 @@ def get_gl_entries_on_asset_disposal(
"credit_in_account_currency": asset.gross_purchase_amount, "credit_in_account_currency": asset.gross_purchase_amount,
"credit": asset.gross_purchase_amount, "credit": asset.gross_purchase_amount,
"cost_center": depreciation_cost_center, "cost_center": depreciation_cost_center,
"posting_date": getdate(), "posting_date": date,
}, },
item=asset, item=asset,
), ),
@ -513,7 +519,7 @@ def get_gl_entries_on_asset_disposal(
"debit_in_account_currency": accumulated_depr_amount, "debit_in_account_currency": accumulated_depr_amount,
"debit": accumulated_depr_amount, "debit": accumulated_depr_amount,
"cost_center": depreciation_cost_center, "cost_center": depreciation_cost_center,
"posting_date": getdate(), "posting_date": date,
}, },
item=asset, item=asset,
), ),
@ -522,7 +528,7 @@ def get_gl_entries_on_asset_disposal(
profit_amount = flt(selling_amount) - flt(value_after_depreciation) profit_amount = flt(selling_amount) - flt(value_after_depreciation)
if profit_amount: if profit_amount:
get_profit_gl_entries( get_profit_gl_entries(
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date
) )
if voucher_type and voucher_no: if voucher_type and voucher_no:
@ -556,8 +562,11 @@ def get_asset_details(asset, finance_book=None):
def get_profit_gl_entries( def get_profit_gl_entries(
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date=None
): ):
if not date:
date = getdate()
debit_or_credit = "debit" if profit_amount < 0 else "credit" debit_or_credit = "debit" if profit_amount < 0 else "credit"
gl_entries.append( gl_entries.append(
asset.get_gl_dict( asset.get_gl_dict(
@ -566,7 +575,7 @@ def get_profit_gl_entries(
"cost_center": depreciation_cost_center, "cost_center": depreciation_cost_center,
debit_or_credit: abs(profit_amount), debit_or_credit: abs(profit_amount),
debit_or_credit + "_in_account_currency": abs(profit_amount), debit_or_credit + "_in_account_currency": abs(profit_amount),
"posting_date": getdate(), "posting_date": date,
}, },
item=asset, item=asset,
) )

View File

@ -356,6 +356,83 @@ class TestAsset(AssetSetup):
si.cancel() si.cancel()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated") self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
def test_gle_made_by_asset_sale_for_existing_asset(self):
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
asset = create_asset(
calculate_depreciation=1,
available_for_use_date="2020-04-01",
purchase_date="2020-04-01",
expected_value_after_useful_life=0,
total_number_of_depreciations=5,
number_of_depreciations_booked=2,
frequency_of_depreciation=12,
depreciation_start_date="2023-03-31",
opening_accumulated_depreciation=24000,
gross_purchase_amount=60000,
submit=1,
)
expected_depr_values = [
["2023-03-31", 12000, 36000],
["2024-03-31", 12000, 48000],
["2025-03-31", 12000, 60000],
]
first_asset_depr_schedule = get_depr_schedule(asset.name, "Active")
for i, schedule in enumerate(first_asset_depr_schedule):
self.assertEqual(getdate(expected_depr_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_depr_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_depr_values[i][2], schedule.accumulated_depreciation_amount)
post_depreciation_entries(date="2023-03-31")
si = create_sales_invoice(
item_code="Macbook Pro", asset=asset.name, qty=1, rate=40000, posting_date=getdate("2023-05-23")
)
asset.load_from_db()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
expected_values = [["2023-03-31", 12000, 36000], ["2023-05-23", 1742.47, 37742.47]]
second_asset_depr_schedule = get_depr_schedule(asset.name, "Active")
for i, schedule in enumerate(second_asset_depr_schedule):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
expected_gle = (
(
"_Test Accumulated Depreciations - _TC",
37742.47,
0.0,
),
(
"_Test Fixed Asset - _TC",
0.0,
60000.0,
),
(
"_Test Gain/Loss on Asset Disposal - _TC",
0.0,
17742.47,
),
("Debtors - _TC", 40000.0, 0.0),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no = %s
order by account""",
si.name,
)
self.assertSequenceEqual(gle, expected_gle)
def test_asset_with_maintenance_required_status_after_sale(self): def test_asset_with_maintenance_required_status_after_sale(self):
asset = create_asset( asset = create_asset(
calculate_depreciation=1, calculate_depreciation=1,
@ -691,7 +768,7 @@ class TestDepreciationMethods(AssetSetup):
) )
self.assertEqual(asset.status, "Draft") self.assertEqual(asset.status, "Draft")
expected_schedules = [["2032-12-31", 30000.0, 77095.89], ["2033-06-06", 12904.11, 90000.0]] expected_schedules = [["2032-12-31", 42904.11, 90000.0]]
schedules = [ schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] [cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in get_depr_schedule(asset.name, "Draft") for d in get_depr_schedule(asset.name, "Draft")

View File

@ -443,6 +443,7 @@ class AssetCapitalization(StockController):
item.get("finance_book") or self.get("finance_book"), item.get("finance_book") or self.get("finance_book"),
self.get("doctype"), self.get("doctype"),
self.get("name"), self.get("name"),
self.get("posting_date"),
) )
asset.db_set("disposal_date", self.posting_date) asset.db_set("disposal_date", self.posting_date)

View File

@ -252,7 +252,10 @@ class AssetDepreciationSchedule(Document):
# if asset is being sold or scrapped # if asset is being sold or scrapped
if date_of_disposal: if date_of_disposal:
from_date = asset_doc.available_for_use_date from_date = add_months(
getdate(asset_doc.available_for_use_date),
(asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation),
)
if self.depreciation_schedule: if self.depreciation_schedule:
from_date = self.depreciation_schedule[-1].schedule_date from_date = self.depreciation_schedule[-1].schedule_date
@ -337,7 +340,7 @@ class AssetDepreciationSchedule(Document):
depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life
skip_row = True skip_row = True
if depreciation_amount > 0: if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0:
self.add_depr_schedule_row( self.add_depr_schedule_row(
schedule_date, schedule_date,
depreciation_amount, depreciation_amount,
@ -521,9 +524,11 @@ def get_straight_line_or_manual_depr_amount(asset, row):
) )
# if the Depreciation Schedule is being prepared for the first time # if the Depreciation Schedule is being prepared for the first time
else: else:
return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt( return (
row.total_number_of_depreciations flt(asset.gross_purchase_amount)
) - flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
def get_wdv_or_dd_depr_amount( def get_wdv_or_dd_depr_amount(

View File

@ -157,7 +157,7 @@
"party_account_currency", "party_account_currency",
"inter_company_order_reference", "inter_company_order_reference",
"is_old_subcontracting_flow", "is_old_subcontracting_flow",
"dashboard" "connections_tab"
], ],
"fields": [ "fields": [
{ {
@ -1171,7 +1171,6 @@
"depends_on": "is_internal_supplier", "depends_on": "is_internal_supplier",
"fieldname": "set_from_warehouse", "fieldname": "set_from_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Set From Warehouse", "label": "Set From Warehouse",
"options": "Warehouse" "options": "Warehouse"
}, },
@ -1185,12 +1184,6 @@
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "More Info" "label": "More Info"
}, },
{
"fieldname": "dashboard",
"fieldtype": "Tab Break",
"label": "Dashboard",
"show_dashboard": 1
},
{ {
"fieldname": "column_break_7", "fieldname": "column_break_7",
"fieldtype": "Column Break" "fieldtype": "Column Break"
@ -1266,13 +1259,19 @@
"fieldname": "shipping_address_section", "fieldname": "shipping_address_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Shipping Address" "label": "Shipping Address"
},
{
"fieldname": "connections_tab",
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2023-05-07 20:18:09.196799", "modified": "2023-05-24 11:16:41.195340",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -392,6 +392,9 @@ class AccountsController(TransactionBase):
) )
def validate_inter_company_reference(self): def validate_inter_company_reference(self):
if self.get("is_return"):
return
if self.doctype not in ("Purchase Invoice", "Purchase Receipt"): if self.doctype not in ("Purchase Invoice", "Purchase Receipt"):
return return
@ -1679,6 +1682,9 @@ class AccountsController(TransactionBase):
d.base_payment_amount = flt( d.base_payment_amount = flt(
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount") d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
) )
else:
self.fetch_payment_terms_from_order(po_or_so, doctype)
self.ignore_default_payment_terms_template = 1
def get_order_details(self): def get_order_details(self):
if self.doctype == "Sales Invoice": if self.doctype == "Sales Invoice":

View File

@ -53,13 +53,17 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
doctype = "Lead" doctype = "Lead"
fields = get_fields(doctype, ["name", "lead_name", "company_name"]) fields = get_fields(doctype, ["name", "lead_name", "company_name"])
searchfields = frappe.get_meta(doctype).get_search_fields()
searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
return frappe.db.sql( return frappe.db.sql(
"""select {fields} from `tabLead` """select {fields} from `tabLead`
where docstatus < 2 where docstatus < 2
and ifnull(status, '') != 'Converted' and ifnull(status, '') != 'Converted'
and ({key} like %(txt)s and ({key} like %(txt)s
or lead_name like %(txt)s or lead_name like %(txt)s
or company_name like %(txt)s) or company_name like %(txt)s
or {scond})
{mcond} {mcond}
order by order by
(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end), (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
@ -68,7 +72,12 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
idx desc, idx desc,
name, lead_name name, lead_name
limit %(page_len)s offset %(start)s""".format( limit %(page_len)s offset %(start)s""".format(
**{"fields": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)} **{
"fields": ", ".join(fields),
"key": searchfield,
"scond": searchfields,
"mcond": get_match_cond(doctype),
}
), ),
{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
) )

View File

@ -168,7 +168,7 @@ class SellingController(StockController):
self.round_floats_in(sales_person) self.round_floats_in(sales_person)
sales_person.allocated_amount = flt( sales_person.allocated_amount = flt(
self.amount_eligible_for_commission * sales_person.allocated_percentage / 100.0, flt(self.amount_eligible_for_commission) * sales_person.allocated_percentage / 100.0,
self.precision("allocated_amount", sales_person), self.precision("allocated_amount", sales_person),
) )

View File

@ -449,8 +449,22 @@ class StockController(AccountsController):
"Delivery Note", "Delivery Note",
"Stock Entry", "Stock Entry",
]: ]:
if (sl_dict.actual_qty > 0 and self.doctype in ["Purchase Invoice", "Purchase Receipt"]) or ( if (
sl_dict.actual_qty < 0 and self.doctype in ["Sales Invoice", "Delivery Note", "Stock Entry"] (
sl_dict.actual_qty > 0
and not self.get("is_return")
or sl_dict.actual_qty < 0
and self.get("is_return")
)
and self.doctype in ["Purchase Invoice", "Purchase Receipt"]
) or (
(
sl_dict.actual_qty < 0
and not self.get("is_return")
or sl_dict.actual_qty > 0
and self.get("is_return")
)
and self.doctype in ["Sales Invoice", "Delivery Note", "Stock Entry"]
): ):
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname) sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
else: else:
@ -756,6 +770,9 @@ class StockController(AccountsController):
} }
) )
if self.docstatus == 2:
force = True
if force or future_sle_exists(args) or repost_required_for_queue(self): if force or future_sle_exists(args) or repost_required_for_queue(self):
item_based_reposting = cint( item_based_reposting = cint(
frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting") frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")

View File

@ -689,7 +689,6 @@ class SubcontractingController(StockController):
"actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor), "actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor),
"serial_no": cstr(item.rejected_serial_no).strip(), "serial_no": cstr(item.rejected_serial_no).strip(),
"incoming_rate": 0.0, "incoming_rate": 0.0,
"recalculate_rate": 1,
}, },
) )
) )
@ -741,7 +740,7 @@ class SubcontractingController(StockController):
sco_doc = frappe.get_doc("Subcontracting Order", sco) sco_doc = frappe.get_doc("Subcontracting Order", sco)
sco_doc.update_status() sco_doc.update_status()
def set_missing_values_in_additional_costs(self): def calculate_additional_costs(self):
self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
if self.total_additional_costs: if self.total_additional_costs:

View File

@ -36,7 +36,7 @@ class TestSubcontractingController(FrappeTestCase):
sco.remove_empty_rows() sco.remove_empty_rows()
self.assertEqual((len_before - 1), len(sco.service_items)) self.assertEqual((len_before - 1), len(sco.service_items))
def test_set_missing_values_in_additional_costs(self): def test_calculate_additional_costs(self):
sco = get_subcontracting_order(do_not_submit=1) sco = get_subcontracting_order(do_not_submit=1)
rate_without_additional_cost = sco.items[0].rate rate_without_additional_cost = sco.items[0].rate

View File

@ -439,7 +439,7 @@
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-11-09 15:02:44.490731", "modified": "2023-05-23 09:56:43.826602",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Job Card", "name": "Job Card",

View File

@ -730,7 +730,7 @@ class JobCard(Document):
self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0] self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
if self.docstatus < 2: if self.docstatus < 2:
if self.for_quantity <= self.transferred_qty: if flt(self.for_quantity) <= flt(self.transferred_qty):
self.status = "Material Transferred" self.status = "Material Transferred"
if self.time_logs: if self.time_logs:

View File

@ -451,10 +451,14 @@ frappe.ui.form.on("Material Request Plan Item", {
for_warehouse: row.warehouse for_warehouse: row.warehouse
}, },
callback: function(r) { callback: function(r) {
let {projected_qty, actual_qty} = r.message; if (r.message) {
let {projected_qty, actual_qty} = r.message[0];
frappe.model.set_value(cdt, cdn, 'projected_qty', projected_qty); frappe.model.set_value(cdt, cdn, {
frappe.model.set_value(cdt, cdn, 'actual_qty', actual_qty); 'projected_qty': projected_qty,
'actual_qty': actual_qty
});
}
} }
}) })
} }

View File

@ -35,8 +35,12 @@
"section_break_25", "section_break_25",
"prod_plan_references", "prod_plan_references",
"section_break_24", "section_break_24",
"get_sub_assembly_items",
"combine_sub_items", "combine_sub_items",
"section_break_ucc4",
"skip_available_sub_assembly_item",
"column_break_igxl",
"get_sub_assembly_items",
"section_break_g4ip",
"sub_assembly_items", "sub_assembly_items",
"download_materials_request_plan_section_section", "download_materials_request_plan_section_section",
"download_materials_required", "download_materials_required",
@ -351,12 +355,12 @@
{ {
"fieldname": "section_break_24", "fieldname": "section_break_24",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1 "hide_border": 1,
"label": "Sub Assembly Items"
}, },
{ {
"fieldname": "sub_assembly_items", "fieldname": "sub_assembly_items",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Sub Assembly Items",
"no_copy": 1, "no_copy": 1,
"options": "Production Plan Sub Assembly Item" "options": "Production Plan Sub Assembly Item"
}, },
@ -392,13 +396,33 @@
"fieldname": "download_materials_request_plan_section_section", "fieldname": "download_materials_request_plan_section_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Download Materials Request Plan Section" "label": "Download Materials Request Plan Section"
},
{
"default": "0",
"description": "System consider the projected quantity to check available or will be available sub-assembly items ",
"fieldname": "skip_available_sub_assembly_item",
"fieldtype": "Check",
"label": "Skip Available Sub Assembly Items"
},
{
"fieldname": "section_break_ucc4",
"fieldtype": "Column Break",
"hide_border": 1
},
{
"fieldname": "section_break_g4ip",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_igxl",
"fieldtype": "Column Break"
} }
], ],
"icon": "fa fa-calendar", "icon": "fa fa-calendar",
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2023-03-31 10:30:48.118932", "modified": "2023-05-22 23:36:31.770517",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan", "name": "Production Plan",

View File

@ -718,7 +718,9 @@ class ProductionPlan(Document):
frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx)) frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
bom_data = [] bom_data = []
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
warehouse = row.warehouse if self.skip_available_sub_assembly_item else None
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
sub_assembly_items_store.extend(bom_data) sub_assembly_items_store.extend(bom_data)
@ -894,7 +896,9 @@ def download_raw_materials(doc, warehouses=None):
build_csv_response(item_list, doc.name) build_csv_response(item_list, doc.name)
def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1): def get_exploded_items(
item_details, company, bom_no, include_non_stock_items, planned_qty=1, doc=None
):
bei = frappe.qb.DocType("BOM Explosion Item") bei = frappe.qb.DocType("BOM Explosion Item")
bom = frappe.qb.DocType("BOM") bom = frappe.qb.DocType("BOM")
item = frappe.qb.DocType("Item") item = frappe.qb.DocType("Item")
@ -1271,6 +1275,12 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
include_safety_stock = doc.get("include_safety_stock") include_safety_stock = doc.get("include_safety_stock")
so_item_details = frappe._dict() so_item_details = frappe._dict()
sub_assembly_items = {}
if doc.get("skip_available_sub_assembly_item"):
for d in doc.get("sub_assembly_items"):
sub_assembly_items.setdefault((d.get("production_item"), d.get("bom_no")), d.get("qty"))
for data in po_items: for data in po_items:
if not data.get("include_exploded_items") and doc.get("sub_assembly_items"): if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
data["include_exploded_items"] = 1 data["include_exploded_items"] = 1
@ -1296,10 +1306,24 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx"))) frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
if bom_no: if bom_no:
if data.get("include_exploded_items") and include_subcontracted_items: if (
data.get("include_exploded_items")
and doc.get("sub_assembly_items")
and doc.get("skip_available_sub_assembly_item")
):
item_details = get_raw_materials_of_sub_assembly_items(
item_details,
company,
bom_no,
include_non_stock_items,
sub_assembly_items,
planned_qty=planned_qty,
)
elif data.get("include_exploded_items") and include_subcontracted_items:
# fetch exploded items from BOM # fetch exploded items from BOM
item_details = get_exploded_items( item_details = get_exploded_items(
item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty, doc=doc
) )
else: else:
item_details = get_subitems( item_details = get_subitems(
@ -1456,12 +1480,22 @@ def get_item_data(item_code):
} }
def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0): def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, company, warehouse=None, indent=0):
data = get_bom_children(parent=bom_no) data = get_bom_children(parent=bom_no)
for d in data: for d in data:
if d.expandable: if d.expandable:
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item") parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty) stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
if warehouse:
bin_dict = get_bin_details(d, company, for_warehouse=warehouse)
if bin_dict and bin_dict[0].projected_qty > 0:
if bin_dict[0].projected_qty > stock_qty:
continue
else:
stock_qty = stock_qty - bin_dict[0].projected_qty
bom_data.append( bom_data.append(
frappe._dict( frappe._dict(
{ {
@ -1481,7 +1515,7 @@ def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
) )
if d.value: if d.value:
get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent + 1) get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1)
def set_default_warehouses(row, default_warehouses): def set_default_warehouses(row, default_warehouses):
@ -1519,3 +1553,68 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
) )
return reserved_qty_for_production_plan - reserved_qty_for_production return reserved_qty_for_production_plan - reserved_qty_for_production
def get_raw_materials_of_sub_assembly_items(
item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
):
bei = frappe.qb.DocType("BOM Item")
bom = frappe.qb.DocType("BOM")
item = frappe.qb.DocType("Item")
item_default = frappe.qb.DocType("Item Default")
item_uom = frappe.qb.DocType("UOM Conversion Detail")
items = (
frappe.qb.from_(bei)
.join(bom)
.on(bom.name == bei.parent)
.join(item)
.on(item.name == bei.item_code)
.left_join(item_default)
.on((item_default.parent == item.name) & (item_default.company == company))
.left_join(item_uom)
.on((item.name == item_uom.parent) & (item_uom.uom == item.purchase_uom))
.select(
(IfNull(Sum(bei.stock_qty / IfNull(bom.quantity, 1)), 0) * planned_qty).as_("qty"),
item.item_name,
item.name.as_("item_code"),
bei.description,
bei.stock_uom,
bei.bom_no,
item.min_order_qty,
bei.source_warehouse,
item.default_material_request_type,
item.min_order_qty,
item_default.default_warehouse,
item.purchase_uom,
item_uom.conversion_factor,
item.safety_stock,
)
.where(
(bei.docstatus == 1)
& (bom.name == bom_no)
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
)
.groupby(bei.item_code, bei.stock_uom)
).run(as_dict=True)
for item in items:
key = (item.item_code, item.bom_no)
if item.bom_no and key in sub_assembly_items:
planned_qty = flt(sub_assembly_items[key])
get_raw_materials_of_sub_assembly_items(
item_details,
company,
item.bom_no,
include_non_stock_items,
sub_assembly_items,
planned_qty=planned_qty,
)
else:
if not item.conversion_factor and item.purchase_uom:
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
item_details.setdefault(item.get("item_code"), item)
return item_details

View File

@ -926,6 +926,50 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(after_qty, before_qty) self.assertEqual(after_qty, before_qty)
def test_skip_available_qty_for_sub_assembly_items(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
bom_tree = {
"Fininshed Goods1 For SUB Test": {
"SubAssembly1 For SUB Test": {"ChildPart1 For SUB Test": {}},
"SubAssembly2 For SUB Test": {},
}
}
parent_bom = create_nested_bom(bom_tree, prefix="")
plan = create_production_plan(
item_code=parent_bom.item,
planned_qty=10,
ignore_existing_ordered_qty=1,
do_not_submit=1,
skip_available_sub_assembly_item=1,
warehouse="_Test Warehouse - _TC",
)
make_stock_entry(
item_code="SubAssembly1 For SUB Test",
qty=5,
rate=100,
target="_Test Warehouse - _TC",
)
self.assertTrue(plan.skip_available_sub_assembly_item)
plan.get_sub_assembly_items()
for row in plan.sub_assembly_items:
if row.production_item == "SubAssembly1 For SUB Test":
self.assertEqual(row.qty, 5)
mr_items = get_items_for_material_requests(plan.as_dict())
for row in mr_items:
row = frappe._dict(row)
if row.item_code == "ChildPart1 For SUB Test":
self.assertEqual(row.quantity, 5)
if row.item_code == "SubAssembly2 For SUB Test":
self.assertEqual(row.quantity, 10)
def create_production_plan(**args): def create_production_plan(**args):
""" """
@ -945,6 +989,7 @@ def create_production_plan(**args):
"include_subcontracted_items": args.include_subcontracted_items or 0, "include_subcontracted_items": args.include_subcontracted_items or 0,
"ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0, "ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0,
"get_items_from": "Sales Order", "get_items_from": "Sales Order",
"skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0,
} }
) )
@ -958,6 +1003,7 @@ def create_production_plan(**args):
"planned_qty": args.planned_qty or 1, "planned_qty": args.planned_qty or 1,
"planned_start_date": args.planned_start_date or now_datetime(), "planned_start_date": args.planned_start_date or now_datetime(),
"stock_uom": args.stock_uom or "Nos", "stock_uom": args.stock_uom or "Nos",
"warehouse": args.warehouse,
}, },
) )

View File

@ -28,7 +28,11 @@
"uom", "uom",
"stock_uom", "stock_uom",
"column_break_22", "column_break_22",
"description" "description",
"section_break_4rxf",
"actual_qty",
"column_break_xfhm",
"projected_qty"
], ],
"fields": [ "fields": [
{ {
@ -183,12 +187,34 @@
"fieldtype": "Datetime", "fieldtype": "Datetime",
"in_list_view": 1, "in_list_view": 1,
"label": "Schedule Date" "label": "Schedule Date"
},
{
"fieldname": "section_break_4rxf",
"fieldtype": "Section Break"
},
{
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": "Actual Qty",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "column_break_xfhm",
"fieldtype": "Column Break"
},
{
"fieldname": "projected_qty",
"fieldtype": "Float",
"label": "Projected Qty",
"no_copy": 1,
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-11-28 13:50:15.116082", "modified": "2023-05-22 17:52:34.708879",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan Sub Assembly Item", "name": "Production Plan Sub Assembly Item",

View File

@ -326,7 +326,7 @@ erpnext.patches.v13_0.update_docs_link
erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
erpnext.patches.v14_0.update_closing_balances #10-05-2023 erpnext.patches.v14_0.update_closing_balances #17-05-2023
execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
# below migration patches should always run last # below migration patches should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.patches.v14_0.migrate_gl_to_payment_ledger

View File

@ -7,4 +7,6 @@ def execute():
frappe.reload_doc("manufacturing", "doctype", "work_order") frappe.reload_doc("manufacturing", "doctype", "work_order")
frappe.reload_doc("manufacturing", "doctype", "work_order_item") frappe.reload_doc("manufacturing", "doctype", "work_order_item")
frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""") frappe.db.sql(
"""UPDATE `tabWork Order Item` SET amount = ifnull(rate, 0.0) * ifnull(required_qty, 0.0)"""
)

View File

@ -13,8 +13,8 @@ from erpnext.accounts.utils import get_fiscal_year
def execute(): def execute():
frappe.db.truncate("Account Closing Balance") frappe.db.truncate("Account Closing Balance")
i = 0
company_wise_order = {} company_wise_order = {}
get_opening_entries = True
for pcv in frappe.db.get_all( for pcv in frappe.db.get_all(
"Period Closing Voucher", "Period Closing Voucher",
fields=["company", "posting_date", "name"], fields=["company", "posting_date", "name"],
@ -29,6 +29,7 @@ def execute():
pcv.posting_date, pcv.fiscal_year, company=pcv.company pcv.posting_date, pcv.fiscal_year, company=pcv.company
)[1] )[1]
# get gl entries against pcv
gl_entries = frappe.db.get_all( gl_entries = frappe.db.get_all(
"GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"]
) )
@ -37,20 +38,31 @@ def execute():
entry["closing_date"] = pcv_doc.posting_date entry["closing_date"] = pcv_doc.posting_date
entry["period_closing_voucher"] = pcv_doc.name entry["period_closing_voucher"] = pcv_doc.name
# get all gl entries for the year
closing_entries = frappe.db.get_all( closing_entries = frappe.db.get_all(
"GL Entry", "GL Entry",
filters={ filters={
"is_cancelled": 0, "is_cancelled": 0,
"voucher_no": ["!=", pcv.name], "voucher_no": ["!=", pcv.name],
"posting_date": ["<=", pcv.posting_date], "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]],
"is_opening": "No",
}, },
fields=["*"], fields=["*"],
) )
if i == 0:
# add opening entries only for the first pcv
closing_entries += frappe.db.get_all(
"GL Entry",
filters={"is_cancelled": 0, "is_opening": "Yes"},
fields=["*"],
)
for entry in closing_entries: for entry in closing_entries:
entry["closing_date"] = pcv_doc.posting_date entry["closing_date"] = pcv_doc.posting_date
entry["period_closing_voucher"] = pcv_doc.name entry["period_closing_voucher"] = pcv_doc.name
make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name) make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name)
company_wise_order[pcv.company].append(pcv.posting_date) company_wise_order[pcv.company].append(pcv.posting_date)
get_opening_entries = False
i += 1

View File

@ -374,6 +374,7 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None
billing_rate = billing_amount / hours billing_rate = billing_amount / hours
target.company = timesheet.company target.company = timesheet.company
target.project = timesheet.parent_project
if customer: if customer:
target.customer = customer target.customer = customer

View File

@ -91,6 +91,12 @@ frappe.ui.form.on("Sales Invoice", {
}); });
frappe.ui.form.on('Purchase Invoice', { frappe.ui.form.on('Purchase Invoice', {
setup: (frm) => {
frm.make_methods = {
'Landed Cost Voucher': function () { frm.trigger('create_landedcost_voucher') },
}
},
mode_of_payment: function(frm) { mode_of_payment: function(frm) {
get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){ get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
frm.set_value('cash_bank_account', account); frm.set_value('cash_bank_account', account);
@ -99,6 +105,20 @@ frappe.ui.form.on('Purchase Invoice', {
payment_terms_template: function() { payment_terms_template: function() {
cur_frm.trigger("disable_due_date"); cur_frm.trigger("disable_due_date");
},
create_landedcost_voucher: function (frm) {
let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
lcv.company = frm.doc.company;
let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice');
lcv_receipt.receipt_document_type = 'Purchase Invoice';
lcv_receipt.receipt_document = frm.doc.name;
lcv_receipt.supplier = frm.doc.supplier;
lcv_receipt.grand_total = frm.doc.grand_total;
lcv.purchase_receipts = [lcv_receipt];
frappe.set_route("Form", lcv.doctype, lcv.name);
} }
}); });

View File

@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
) )
# sales team # sales team
for d in customer.get("sales_team"): for d in customer.get("sales_team") or []:
target.append( target.append(
"sales_team", "sales_team",
{ {

View File

@ -388,7 +388,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
} }
} }
// payment request // payment request
if(flt(doc.per_billed)<100) { if(flt(doc.per_billed, precision('per_billed', doc)) < 100 + frappe.boot.sysdefaults.over_billing_allowance) {
this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create')); this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create'));
this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create')); this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create'));
} }

View File

@ -2,9 +2,11 @@
"creation": "2021-11-23 12:00:36.138824", "creation": "2021-11-23 12:00:36.138824",
"docstatus": 0, "docstatus": 0,
"doctype": "Form Tour", "doctype": "Form Tour",
"first_document": 0,
"idx": 0, "idx": 0,
"include_name_field": 0,
"is_standard": 1, "is_standard": 1,
"modified": "2021-11-23 12:02:48.010298", "modified": "2023-05-23 12:51:48.684517",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Quotation", "name": "Quotation",
@ -14,51 +16,43 @@
"steps": [ "steps": [
{ {
"description": "Select a customer or lead for whom this quotation is being prepared. Let's select a Customer.", "description": "Select a customer or lead for whom this quotation is being prepared. Let's select a Customer.",
"field": "",
"fieldname": "quotation_to", "fieldname": "quotation_to",
"fieldtype": "Link", "fieldtype": "Link",
"has_next_condition": 0, "has_next_condition": 0,
"is_table_field": 0, "is_table_field": 0,
"label": "Quotation To", "label": "Quotation To",
"parent_field": "",
"position": "Right", "position": "Right",
"title": "Quotation To" "title": "Quotation To"
}, },
{ {
"description": "Select a specific Customer to whom this quotation will be sent.", "description": "Select a specific Customer to whom this quotation will be sent.",
"field": "",
"fieldname": "party_name", "fieldname": "party_name",
"fieldtype": "Dynamic Link", "fieldtype": "Dynamic Link",
"has_next_condition": 0, "has_next_condition": 0,
"is_table_field": 0, "is_table_field": 0,
"label": "Party", "label": "Party",
"parent_field": "",
"position": "Right", "position": "Right",
"title": "Party" "title": "Party"
}, },
{ {
"child_doctype": "Quotation Item", "child_doctype": "Quotation Item",
"description": "Select an item for which you will be quoting a price.", "description": "Select an item for which you will be quoting a price.",
"field": "",
"fieldname": "items", "fieldname": "items",
"fieldtype": "Table", "fieldtype": "Table",
"has_next_condition": 0, "has_next_condition": 0,
"is_table_field": 0, "is_table_field": 0,
"label": "Items", "label": "Items",
"parent_field": "",
"parent_fieldname": "items", "parent_fieldname": "items",
"position": "Bottom", "position": "Bottom",
"title": "Items" "title": "Items"
}, },
{ {
"description": "You can select pre-populated Sales Taxes and Charges from here.", "description": "You can select pre-populated Sales Taxes and Charges from here.",
"field": "",
"fieldname": "taxes", "fieldname": "taxes",
"fieldtype": "Table", "fieldtype": "Table",
"has_next_condition": 0, "has_next_condition": 0,
"is_table_field": 0, "is_table_field": 0,
"label": "Sales Taxes and Charges", "label": "Sales Taxes and Charges",
"parent_field": "",
"position": "Bottom", "position": "Bottom",
"title": "Sales Taxes and Charges" "title": "Sales Taxes and Charges"
} }

View File

@ -299,7 +299,8 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
} }
batch_no(doc, cdt, cdn) { batch_no(doc, cdt, cdn) {
var me = this; super.batch_no(doc, cdt, cdn);
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);
if (item.serial_no) { if (item.serial_no) {
@ -378,10 +379,6 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
} }
} }
batch_no(doc, cdt, cdn) {
super.batch_no(doc, cdt, cdn);
}
qty(doc, cdt, cdn) { qty(doc, cdt, cdn) {
super.qty(doc, cdt, cdn); super.qty(doc, cdt, cdn);

View File

@ -257,7 +257,9 @@ def get_employee_email(employee_doc):
def get_holiday_list_for_employee(employee, raise_exception=True): def get_holiday_list_for_employee(employee, raise_exception=True):
if employee: if employee:
holiday_list, company = frappe.db.get_value("Employee", employee, ["holiday_list", "company"]) holiday_list, company = frappe.get_cached_value(
"Employee", employee, ["holiday_list", "company"]
)
else: else:
holiday_list = "" holiday_list = ""
company = frappe.db.get_single_value("Global Defaults", "default_company") company = frappe.db.get_single_value("Global Defaults", "default_company")

View File

@ -115,6 +115,8 @@ def is_holiday(holiday_list, date=None):
if date is None: if date is None:
date = today() date = today()
if holiday_list: if holiday_list:
return bool(frappe.get_all("Holiday List", dict(name=holiday_list, holiday_date=date))) return bool(
frappe.db.exists("Holiday", {"parent": holiday_list, "holiday_date": date}, cache=True)
)
else: else:
return False return False

View File

@ -22,24 +22,15 @@
"creation": "2021-11-22 12:19:15.888642", "creation": "2021-11-22 12:19:15.888642",
"docstatus": 0, "docstatus": 0,
"doctype": "Module Onboarding", "doctype": "Module Onboarding",
"documentation_url": "https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/company-setup", "documentation_url": "https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/company-setup",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2022-06-07 14:31:00.575193", "modified": "2023-05-23 13:20:19.703506",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Home", "name": "Home",
"owner": "Administrator", "owner": "Administrator",
"steps": [ "steps": [
{
"step": "Company Set Up"
},
{
"step": "Navigation Help"
},
{
"step": "Data import"
},
{ {
"step": "Create an Item" "step": "Create an Item"
}, },
@ -47,16 +38,10 @@
"step": "Create a Customer" "step": "Create a Customer"
}, },
{ {
"step": "Create a Supplier" "step": "Create Your First Sales Invoice"
},
{
"step": "Create a Quotation"
},
{
"step": "Letterhead"
} }
], ],
"subtitle": "Company, Item, Customer, Supplier, Navigation Help, Data Import, Letter Head, Quotation", "subtitle": "Item, Customer, Supplier and Quotation",
"success_message": "Masters are all set up!", "success_message": "You're ready to start your journey with ERPNext",
"title": "Let's Set Up Some Masters" "title": "Let's begin your journey with ERPNext"
} }

View File

@ -5,11 +5,11 @@
"description": "# Set Up a Company\n\nA company is a legal entity for which you will set up your books of account and create accounting transactions. In ERPNext, you can create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company.\n", "description": "# Set Up a Company\n\nA company is a legal entity for which you will set up your books of account and create accounting transactions. In ERPNext, you can create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company.\n",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2021-12-15 14:22:18.317423", "modified": "2023-05-15 09:18:42.895537",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Company Set Up", "name": "Company Set Up",
"owner": "Administrator", "owner": "Administrator",

View File

@ -5,17 +5,17 @@
"description": "# Create a Customer\n\nThe Customer master is at the heart of your sales transactions. Customers are linked in Quotations, Sales Orders, Invoices, and Payments. Customers can be either numbered or identified by name (you would typically do this based on the number of customers you have).\n\nThrough Customer\u2019s master, you can effectively track essentials like:\n - Customer\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n", "description": "# Create a Customer\n\nThe Customer master is at the heart of your sales transactions. Customers are linked in Quotations, Sales Orders, Invoices, and Payments. Customers can be either numbered or identified by name (you would typically do this based on the number of customers you have).\n\nThrough Customer\u2019s master, you can effectively track essentials like:\n - Customer\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2021-12-15 14:20:31.197564", "modified": "2023-05-23 12:45:55.138580",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create a Customer", "name": "Create a Customer",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Customer", "reference_document": "Customer",
"show_form_tour": 0, "show_form_tour": 0,
"show_full_form": 0, "show_full_form": 0,
"title": "Manage Customers", "title": "Create a Customer",
"validate_action": 1 "validate_action": 1
} }

View File

@ -5,11 +5,11 @@
"description": "# Create a Quotation\n\nLet\u2019s get started with business transactions by creating your first Quotation. You can create a Quotation for an existing customer or a prospect. It will be an approved document, with items you sell and the proposed price + taxes applied. After completing the instructions, you will get a Quotation in a ready to share print format.", "description": "# Create a Quotation\n\nLet\u2019s get started with business transactions by creating your first Quotation. You can create a Quotation for an existing customer or a prospect. It will be an approved document, with items you sell and the proposed price + taxes applied. After completing the instructions, you will get a Quotation in a ready to share print format.",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2021-12-15 14:21:31.675330", "modified": "2023-05-15 09:18:42.984170",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create a Quotation", "name": "Create a Quotation",
"owner": "Administrator", "owner": "Administrator",

View File

@ -5,17 +5,17 @@
"description": "# Create a Supplier\n\nAlso known as Vendor, is a master at the center of your purchase transactions. Suppliers are linked in Request for Quotation, Purchase Orders, Receipts, and Payments. Suppliers can be either numbered or identified by name.\n\nThrough Supplier\u2019s master, you can effectively track essentials like:\n - Supplier\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n", "description": "# Create a Supplier\n\nAlso known as Vendor, is a master at the center of your purchase transactions. Suppliers are linked in Request for Quotation, Purchase Orders, Receipts, and Payments. Suppliers can be either numbered or identified by name.\n\nThrough Supplier\u2019s master, you can effectively track essentials like:\n - Supplier\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2021-12-15 14:21:23.518301", "modified": "2023-05-19 15:32:55.069257",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create a Supplier", "name": "Create a Supplier",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Supplier", "reference_document": "Supplier",
"show_form_tour": 0, "show_form_tour": 0,
"show_full_form": 0, "show_full_form": 0,
"title": "Manage Suppliers", "title": "Create a Supplier",
"validate_action": 1 "validate_action": 1
} }

View File

@ -6,18 +6,18 @@
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"form_tour": "Item General", "form_tour": "Item General",
"idx": 0, "idx": 1,
"intro_video_url": "", "intro_video_url": "",
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2021-12-15 14:19:56.297772", "modified": "2023-05-23 12:43:08.484206",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create an Item", "name": "Create an Item",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Item", "reference_document": "Item",
"show_form_tour": 1, "show_form_tour": 1,
"show_full_form": 1, "show_full_form": 0,
"title": "Manage Items", "title": "Create an Item",
"validate_action": 1 "validate_action": 1
} }

View File

@ -0,0 +1,20 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:48:21.019019",
"description": "# All about sales invoice\n\nA Sales Invoice is a bill that you send to your Customers against which the Customer makes the payment. Sales Invoice is an accounting transaction. On submission of Sales Invoice, the system updates the receivable and books income against a Customer Account.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2023-05-22 21:20:15.589644",
"modified_by": "Administrator",
"name": "Create Your First Sales Invoice",
"owner": "Administrator",
"reference_document": "Sales Invoice",
"show_form_tour": 1,
"show_full_form": 1,
"title": "Create Your First Sales Invoice ",
"validate_action": 1
}

View File

@ -5,11 +5,11 @@
"description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc).", "description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc).",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2022-06-07 14:28:51.390813", "modified": "2023-05-15 09:18:42.962231",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Data import", "name": "Data import",
"owner": "Administrator", "owner": "Administrator",

View File

@ -5,11 +5,11 @@
"description": "# Create a Letter Head\n\nA Letter Head contains your organization's name, logo, address, etc which appears at the header and footer portion in documents. You can learn more about Setting up Letter Head in ERPNext here.\n", "description": "# Create a Letter Head\n\nA Letter Head contains your organization's name, logo, address, etc which appears at the header and footer portion in documents. You can learn more about Setting up Letter Head in ERPNext here.\n",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2021-12-15 14:21:39.037742", "modified": "2023-05-15 09:18:42.995184",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Letterhead", "name": "Letterhead",
"owner": "Administrator", "owner": "Administrator",

View File

@ -2,14 +2,14 @@
"action": "Watch Video", "action": "Watch Video",
"action_label": "Learn about Navigation options", "action_label": "Learn about Navigation options",
"creation": "2021-11-22 12:09:52.233872", "creation": "2021-11-22 12:09:52.233872",
"description": "# Navigation in ERPNext\n\nEase of navigating and browsing around the ERPNext is one of our core strengths. In the following video, you will learn how to reach a specific feature in ERPNext via module page or awesome bar\u2019s shortcut.\n", "description": "# Navigation in ERPNext\n\nEase of navigating and browsing around the ERPNext is one of our core strengths. In the following video, you will learn how to reach a specific feature in ERPNext via module page or AwesomeBar.",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 1,
"is_complete": 0, "is_complete": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2022-06-07 14:28:00.901082", "modified": "2023-05-16 12:53:25.939908",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Navigation Help", "name": "Navigation Help",
"owner": "Administrator", "owner": "Administrator",

View File

@ -21,6 +21,10 @@ def boot_session(bootinfo):
bootinfo.sysdefaults.allow_stale = cint( bootinfo.sysdefaults.allow_stale = cint(
frappe.db.get_single_value("Accounts Settings", "allow_stale") frappe.db.get_single_value("Accounts Settings", "allow_stale")
) )
bootinfo.sysdefaults.over_billing_allowance = frappe.db.get_single_value(
"Accounts Settings", "over_billing_allowance"
)
bootinfo.sysdefaults.quotation_valid_till = cint( bootinfo.sysdefaults.quotation_valid_till = cint(
frappe.db.get_single_value("CRM Settings", "default_valid_till") frappe.db.get_single_value("CRM Settings", "default_valid_till")
) )

View File

@ -4,6 +4,7 @@
import frappe import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from frappe.utils import nowdate, nowtime
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import ( from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
@ -257,6 +258,8 @@ class TestInventoryDimension(FrappeTestCase):
) )
def test_for_purchase_sales_and_stock_transaction(self): def test_for_purchase_sales_and_stock_transaction(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
create_inventory_dimension( create_inventory_dimension(
reference_document="Store", reference_document="Store",
type_of_transaction="Outward", type_of_transaction="Outward",
@ -319,6 +322,98 @@ class TestInventoryDimension(FrappeTestCase):
self.assertEqual(entries[0].store, "Store 2") self.assertEqual(entries[0].store, "Store 2")
self.assertEqual(entries[0].actual_qty, -10.0) self.assertEqual(entries[0].actual_qty, -10.0)
return_dn = make_return_doc("Delivery Note", dn_doc.name)
return_dn.submit()
entries = get_voucher_sl_entries(return_dn.name, ["warehouse", "store", "actual_qty"])
self.assertEqual(entries[0].warehouse, warehouse)
self.assertEqual(entries[0].store, "Store 2")
self.assertEqual(entries[0].actual_qty, 10.0)
se_doc = make_stock_entry(
item_code=item_code, qty=10, from_warehouse=warehouse, to_warehouse=warehouse, do_not_save=True
)
se_doc.items[0].store = "Store 2"
se_doc.items[0].to_store = "Store 1"
se_doc.save()
se_doc.submit()
return_pr = make_return_doc("Purchase Receipt", pr_doc.name)
return_pr.submit()
entries = get_voucher_sl_entries(return_pr.name, ["warehouse", "store", "actual_qty"])
self.assertEqual(entries[0].warehouse, warehouse)
self.assertEqual(entries[0].store, "Store 1")
self.assertEqual(entries[0].actual_qty, -10.0)
def test_inter_transfer_return_against_inventory_dimension(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
data = prepare_data_for_internal_transfer()
dn_doc = create_delivery_note(
customer=data.customer,
company=data.company,
warehouse=data.from_warehouse,
target_warehouse=data.to_warehouse,
qty=5,
cost_center=data.cost_center,
expense_account=data.expense_account,
do_not_submit=True,
)
dn_doc.items[0].store = "Inter Transfer Store 1"
dn_doc.items[0].to_store = "Inter Transfer Store 2"
dn_doc.save()
dn_doc.submit()
for d in get_voucher_sl_entries(dn_doc.name, ["store", "actual_qty"]):
if d.actual_qty > 0:
self.assertEqual(d.store, "Inter Transfer Store 2")
else:
self.assertEqual(d.store, "Inter Transfer Store 1")
pr_doc = make_inter_company_purchase_receipt(dn_doc.name)
pr_doc.items[0].warehouse = data.store_warehouse
pr_doc.items[0].from_store = "Inter Transfer Store 2"
pr_doc.items[0].store = "Inter Transfer Store 3"
pr_doc.save()
pr_doc.submit()
for d in get_voucher_sl_entries(pr_doc.name, ["store", "actual_qty"]):
if d.actual_qty > 0:
self.assertEqual(d.store, "Inter Transfer Store 3")
else:
self.assertEqual(d.store, "Inter Transfer Store 2")
return_doc = make_return_doc("Purchase Receipt", pr_doc.name)
return_doc.submit()
for d in get_voucher_sl_entries(return_doc.name, ["store", "actual_qty"]):
if d.actual_qty > 0:
self.assertEqual(d.store, "Inter Transfer Store 2")
else:
self.assertEqual(d.store, "Inter Transfer Store 3")
dn_doc.load_from_db()
return_doc1 = make_return_doc("Delivery Note", dn_doc.name)
return_doc1.posting_date = nowdate()
return_doc1.posting_time = nowtime()
return_doc1.items[0].target_warehouse = dn_doc.items[0].target_warehouse
return_doc1.items[0].warehouse = dn_doc.items[0].warehouse
return_doc1.save()
return_doc1.submit()
for d in get_voucher_sl_entries(return_doc1.name, ["store", "actual_qty"]):
if d.actual_qty > 0:
self.assertEqual(d.store, "Inter Transfer Store 1")
else:
self.assertEqual(d.store, "Inter Transfer Store 2")
def get_voucher_sl_entries(voucher_no, fields): def get_voucher_sl_entries(voucher_no, fields):
return frappe.get_all( return frappe.get_all(
@ -423,3 +518,79 @@ def create_inventory_dimension(**args):
doc.insert(ignore_permissions=True) doc.insert(ignore_permissions=True)
return doc return doc
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
company = "_Test Company with perpetual inventory"
customer = create_internal_customer(
"_Test Internal Customer 3",
company,
company,
)
supplier = create_internal_supplier(
"_Test Internal Supplier 3",
company,
company,
)
for store in ["Inter Transfer Store 1", "Inter Transfer Store 2", "Inter Transfer Store 3"]:
if not frappe.db.exists("Store", store):
frappe.get_doc({"doctype": "Store", "store_name": store}).insert(ignore_permissions=True)
warehouse = create_warehouse("_Test Internal Warehouse New A", company=company)
to_warehouse = create_warehouse("_Test Internal Warehouse GIT A", company=company)
pr_doc = make_purchase_receipt(
company=company, warehouse=warehouse, qty=10, rate=100, do_not_submit=True
)
pr_doc.items[0].store = "Inter Transfer Store 1"
pr_doc.submit()
if not frappe.db.get_value("Company", company, "unrealized_profit_loss_account"):
account = "Unrealized Profit and Loss - TCP1"
if not frappe.db.exists("Account", account):
frappe.get_doc(
{
"doctype": "Account",
"account_name": "Unrealized Profit and Loss",
"parent_account": "Direct Income - TCP1",
"company": company,
"is_group": 0,
"account_type": "Income Account",
}
).insert()
frappe.db.set_value("Company", company, "unrealized_profit_loss_account", account)
cost_center = frappe.db.get_value("Company", company, "cost_center") or frappe.db.get_value(
"Cost Center", {"company": company}, "name"
)
expene_account = frappe.db.get_value(
"Company", company, "stock_adjustment_account"
) or frappe.db.get_value(
"Account", {"company": company, "account_type": "Expense Account"}, "name"
)
return frappe._dict(
{
"from_warehouse": warehouse,
"to_warehouse": to_warehouse,
"customer": customer,
"supplier": supplier,
"company": company,
"cost_center": cost_center,
"expene_account": expene_account,
"store_warehouse": frappe.db.get_value(
"Warehouse", {"name": ("like", "Store%"), "company": company}, "name"
),
}
)

View File

@ -1,5 +1,5 @@
frappe.listview_settings['Item'] = { frappe.listview_settings['Item'] = {
add_fields: ["item_name", "stock_uom", "item_group", "image", "variant_of", add_fields: ["item_name", "stock_uom", "item_group", "image",
"has_variants", "end_of_life", "disabled"], "has_variants", "end_of_life", "disabled"],
filters: [["disabled", "=", "0"]], filters: [["disabled", "=", "0"]],

View File

@ -29,6 +29,7 @@ class PickList(Document):
self.validate_for_qty() self.validate_for_qty()
def before_save(self): def before_save(self):
self.update_status()
self.set_item_locations() self.set_item_locations()
# set percentage picked in SO # set percentage picked in SO
@ -89,20 +90,20 @@ class PickList(Document):
self.update_reference_qty() self.update_reference_qty()
self.update_sales_order_picking_status() self.update_sales_order_picking_status()
def update_status(self, status=None, update_modified=True): def update_status(self, status=None):
if not status: if not status:
if self.docstatus == 0: if self.docstatus == 0:
status = "Draft" status = "Draft"
elif self.docstatus == 1: elif self.docstatus == 1:
if self.status == "Draft": if target_document_exists(self.name, self.purpose):
status = "Open"
elif target_document_exists(self.name, self.purpose):
status = "Completed" status = "Completed"
else:
status = "Open"
elif self.docstatus == 2: elif self.docstatus == 2:
status = "Cancelled" status = "Cancelled"
if status: if status:
frappe.db.set_value("Pick List", self.name, "status", status, update_modified=update_modified) self.db_set("status", status)
def update_reference_qty(self): def update_reference_qty(self):
packed_items = [] packed_items = []
@ -459,7 +460,7 @@ def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus)
item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty
) )
while remaining_stock_qty > 0 and available_locations: while flt(remaining_stock_qty) > 0 and available_locations:
item_location = available_locations.pop(0) item_location = available_locations.pop(0)
item_location = frappe._dict(item_location) item_location = frappe._dict(item_location)

View File

@ -376,3 +376,19 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
accounts_settings.acc_frozen_upto = "" accounts_settings.acc_frozen_upto = ""
accounts_settings.save() accounts_settings.save()
def test_create_repost_entry_for_cancelled_document(self):
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
get_multiple_items=True,
)
self.assertTrue(pr.docstatus == 1)
self.assertFalse(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name}))
pr.load_from_db()
pr.cancel()
self.assertTrue(pr.docstatus == 2)
self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name}))

View File

@ -33,5 +33,40 @@ frappe.query_reports["Stock and Account Value Comparison"] = {
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.get_today(), "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>
`;
frappe.confirm(__(message), () => {
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
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
}
});
});
});
}
}; };

View File

@ -4,6 +4,7 @@
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import get_link_to_form, parse_json
import erpnext import erpnext
from erpnext.accounts.utils import get_currency_precision, get_stock_accounts from erpnext.accounts.utils import get_currency_precision, get_stock_accounts
@ -134,3 +135,35 @@ def get_columns(filters):
"width": "120", "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(_(f"Reposting entries created: {entries}"))

View File

@ -785,13 +785,21 @@ class update_entries_after(object):
d.db_update() d.db_update()
def update_rate_on_subcontracting_receipt(self, sle, outgoing_rate): def update_rate_on_subcontracting_receipt(self, sle, outgoing_rate):
if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): if frappe.db.exists("Subcontracting Receipt Item", sle.voucher_detail_no):
frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "rate", outgoing_rate) frappe.db.set_value("Subcontracting Receipt Item", sle.voucher_detail_no, "rate", outgoing_rate)
else: else:
frappe.db.set_value( frappe.db.set_value(
"Subcontracting Receipt Supplied Item", sle.voucher_detail_no, "rate", outgoing_rate "Subcontracting Receipt Supplied Item",
sle.voucher_detail_no,
{"rate": outgoing_rate, "amount": abs(sle.actual_qty) * outgoing_rate},
) )
scr = frappe.get_doc("Subcontracting Receipt", sle.voucher_no, for_update=True)
scr.calculate_items_qty_and_amount()
scr.db_update()
for d in scr.items:
d.db_update()
def get_serialized_values(self, sle): def get_serialized_values(self, sle):
incoming_rate = flt(sle.incoming_rate) incoming_rate = flt(sle.incoming_rate)
actual_qty = flt(sle.actual_qty) actual_qty = flt(sle.actual_qty)

View File

@ -256,8 +256,6 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
if isinstance(args, str): if isinstance(args, str):
args = json.loads(args) args = json.loads(args)
voucher_no = args.get("voucher_no") or args.get("name")
in_rate = None in_rate = None
if (args.get("serial_no") or "").strip(): if (args.get("serial_no") or "").strip():
in_rate = get_avg_purchase_rate(args.get("serial_no")) in_rate = get_avg_purchase_rate(args.get("serial_no"))
@ -280,12 +278,13 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
in_rate = ( in_rate = (
_get_fifo_lifo_rate(previous_stock_queue, args.get("qty") or 0, valuation_method) _get_fifo_lifo_rate(previous_stock_queue, args.get("qty") or 0, valuation_method)
if previous_stock_queue if previous_stock_queue
else 0 else None
) )
elif valuation_method == "Moving Average": elif valuation_method == "Moving Average":
in_rate = previous_sle.get("valuation_rate") or 0 in_rate = previous_sle.get("valuation_rate")
if in_rate is None: if in_rate is None:
voucher_no = args.get("voucher_no") or args.get("name")
in_rate = get_valuation_rate( in_rate = get_valuation_rate(
args.get("item_code"), args.get("item_code"),
args.get("warehouse"), args.get("warehouse"),

View File

@ -77,22 +77,22 @@ class SubcontractingOrder(SubcontractingController):
frappe.throw(_(msg)) frappe.throw(_(msg))
def set_missing_values(self): def set_missing_values(self):
self.set_missing_values_in_additional_costs() self.calculate_additional_costs()
self.set_missing_values_in_service_items() self.calculate_service_costs()
self.set_missing_values_in_supplied_items() self.calculate_supplied_items_qty_and_amount()
self.set_missing_values_in_items() self.calculate_items_qty_and_amount()
def set_missing_values_in_service_items(self): def calculate_service_costs(self):
for idx, item in enumerate(self.get("service_items")): for idx, item in enumerate(self.get("service_items")):
self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty
def set_missing_values_in_supplied_items(self): def calculate_supplied_items_qty_and_amount(self):
for item in self.get("items"): for item in self.get("items"):
bom = frappe.get_doc("BOM", item.bom) bom = frappe.get_doc("BOM", item.bom)
rm_cost = sum(flt(rm_item.amount) for rm_item in bom.items) rm_cost = sum(flt(rm_item.amount) for rm_item in bom.items)
item.rm_cost_per_qty = rm_cost / flt(bom.quantity) item.rm_cost_per_qty = rm_cost / flt(bom.quantity)
def set_missing_values_in_items(self): def calculate_items_qty_and_amount(self):
total_qty = total = 0 total_qty = total = 0
for item in self.items: for item in self.items:
item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty) item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty)

View File

@ -76,26 +76,14 @@ frappe.ui.form.on('Subcontracting Receipt', {
} }
}); });
let batch_no_field = frm.get_docfield("items", "batch_no"); let batch_no_field = frm.get_docfield('items', 'batch_no');
if (batch_no_field) { if (batch_no_field) {
batch_no_field.get_route_options_for_new_doc = function(row) { batch_no_field.get_route_options_for_new_doc = function(row) {
return { return {
"item": row.doc.item_code 'item': row.doc.item_code
} }
}; };
} }
frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => {
if (val == 'Material Transferred for Subcontract') {
frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => {
grid_row.docfields.forEach((df) => {
if (df.fieldname == 'consumed_qty') {
df.read_only = 0;
}
});
});
}
});
}, },
refresh: (frm) => { refresh: (frm) => {
@ -157,6 +145,8 @@ frappe.ui.form.on('Subcontracting Receipt', {
} }
}); });
}, __('Get Items From')); }, __('Get Items From'));
frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM');
} }
}, },

View File

@ -28,6 +28,14 @@ class SubcontractingReceipt(SubcontractingController):
}, },
] ]
def onload(self):
self.set_onload(
"backflush_based_on",
frappe.db.get_single_value(
"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
),
)
def update_status_updater_args(self): def update_status_updater_args(self):
if cint(self.is_return): if cint(self.is_return):
self.status_updater.extend( self.status_updater.extend(
@ -113,9 +121,9 @@ class SubcontractingReceipt(SubcontractingController):
@frappe.whitelist() @frappe.whitelist()
def set_missing_values(self): def set_missing_values(self):
self.set_missing_values_in_additional_costs() self.calculate_additional_costs()
self.set_missing_values_in_supplied_items() self.calculate_supplied_items_qty_and_amount()
self.set_missing_values_in_items() self.calculate_items_qty_and_amount()
def set_available_qty_for_consumption(self): def set_available_qty_for_consumption(self):
supplied_items_details = {} supplied_items_details = {}
@ -147,13 +155,13 @@ class SubcontractingReceipt(SubcontractingController):
item.rm_item_code, 0 item.rm_item_code, 0
) )
def set_missing_values_in_supplied_items(self): def calculate_supplied_items_qty_and_amount(self):
for item in self.get("supplied_items") or []: for item in self.get("supplied_items") or []:
item.amount = item.rate * item.consumed_qty item.amount = item.rate * item.consumed_qty
self.set_available_qty_for_consumption() self.set_available_qty_for_consumption()
def set_missing_values_in_items(self): def calculate_items_qty_and_amount(self):
rm_supp_cost = {} rm_supp_cost = {}
for item in self.get("supplied_items") or []: for item in self.get("supplied_items") or []:
if item.reference_name in rm_supp_cost: if item.reference_name in rm_supp_cost:

View File

@ -6,7 +6,7 @@ import copy
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from frappe.utils import cint, flt from frappe.utils import add_days, cint, cstr, flt, today
import erpnext import erpnext
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
@ -26,6 +26,9 @@ from erpnext.controllers.tests.test_subcontracting_controller import (
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
)
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
make_subcontracting_receipt, make_subcontracting_receipt,
) )
@ -528,6 +531,69 @@ class TestSubcontractingReceipt(FrappeTestCase):
# consumed_qty should be (accepted_qty * qty_consumed_per_unit) = (6 * 1) = 6 # consumed_qty should be (accepted_qty * qty_consumed_per_unit) = (6 * 1) = 6
self.assertEqual(scr.supplied_items[0].consumed_qty, 6) self.assertEqual(scr.supplied_items[0].consumed_qty, 6)
def test_supplied_items_cost_after_reposting(self):
# Set Backflush Based On as "BOM"
set_backflush_based_on("BOM")
# Create Material Receipt for RM's
make_stock_entry(
item_code="_Test Item",
qty=100,
target="_Test Warehouse 1 - _TC",
basic_rate=100,
posting_date=add_days(today(), -2),
)
make_stock_entry(
item_code="_Test Item Home Desktop 100",
qty=100,
target="_Test Warehouse 1 - _TC",
basic_rate=100,
)
service_items = [
{
"warehouse": "_Test Warehouse - _TC",
"item_code": "Subcontracted Service Item 1",
"qty": 10,
"rate": 100,
"fg_item": "_Test FG Item",
"fg_item_qty": 10,
},
]
# Create Subcontracting Order
sco = get_subcontracting_order(service_items=service_items)
# Transfer RM's
rm_items = get_rm_items(sco.supplied_items)
itemwise_details = make_stock_in_entry(rm_items=rm_items)
make_stock_transfer_entry(
sco_no=sco.name,
rm_items=rm_items,
itemwise_details=copy.deepcopy(itemwise_details),
)
# Create Subcontracting Receipt
scr = make_subcontracting_receipt(sco.name)
scr.save()
scr.submit()
# Create Backdated Stock Reconciliation
sr = create_stock_reconciliation(
item_code=rm_items[0].get("item_code"),
warehouse="_Test Warehouse 1 - _TC",
qty=100,
rate=50,
posting_date=add_days(today(), -1),
)
# Cost should be updated in Subcontracting Receipt after reposting
prev_cost = scr.supplied_items[0].rate
scr.load_from_db()
self.assertNotEqual(scr.supplied_items[0].rate, prev_cost)
self.assertEqual(scr.supplied_items[0].rate, sr.items[0].valuation_rate)
def make_return_subcontracting_receipt(**args): def make_return_subcontracting_receipt(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -7638,20 +7638,19 @@ Restaurant Order Entry Item,Restaurantbestellzugangsposten,
Served,Serviert, Served,Serviert,
Restaurant Reservation,Restaurant Reservierung, Restaurant Reservation,Restaurant Reservierung,
Waitlisted,Auf der Warteliste, Waitlisted,Auf der Warteliste,
No Show,Keine Show, No Show,Nicht angetreten,
No of People,Nein von Menschen, No of People,Anzahl von Personen,
Reservation Time,Reservierungszeit, Reservation Time,Reservierungszeit,
Reservation End Time,Reservierungsendzeit, Reservation End Time,Reservierungsendzeit,
No of Seats,Anzahl der Sitze, No of Seats,Anzahl der Sitze,
Minimum Seating,Mindestbestuhlung, Minimum Seating,Mindestbestuhlung,
"Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Verkaufskampagne verfolgen: Leads, Angebote, Aufträge usw. von Kampagnen beobachten um die Kapitalverzinsung (RoI) zu messen.", "Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Verkaufskampagne verfolgen: Leads, Angebote, Aufträge usw. von Kampagnen beobachten um die Kapitalverzinsung (RoI) zu messen.",
SAL-CAM-.YYYY.-,SAL-CAM-.YYYY.-,
Campaign Schedules,Kampagnenpläne, Campaign Schedules,Kampagnenpläne,
Buyer of Goods and Services.,Käufer von Waren und Dienstleistungen., Buyer of Goods and Services.,Käufer von Waren und Dienstleistungen.,
CUST-.YYYY.-,CUST-.YYYY.-,
Default Company Bank Account,Standard-Bankkonto des Unternehmens, Default Company Bank Account,Standard-Bankkonto des Unternehmens,
From Lead,Aus Lead, From Lead,Aus Lead,
Account Manager,Buchhalter, Account Manager,Kundenberater,
Accounts Manager,Buchhalter,
Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag, Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag,
Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein, Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein,
Default Price List,Standardpreisliste, Default Price List,Standardpreisliste,
@ -7692,7 +7691,6 @@ Quantity of Items,Anzahl der Artikel,
"Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Fassen Sie eine Gruppe von Artikeln zu einem neuen Artikel zusammen. Dies ist nützlich, wenn Sie bestimmte Artikel zu einem Paket bündeln und einen Bestand an Artikel-Bündeln erhalten und nicht einen Bestand der einzelnen Artikel. Das Artikel-Bündel erhält für das Attribut ""Ist Lagerartikel"" den Wert ""Nein"" und für das Attribut ""Ist Verkaufsartikel"" den Wert ""Ja"". Beispiel: Wenn Sie Laptops und Tragetaschen getrennt verkaufen und einen bestimmten Preis anbieten, wenn der Kunde beides zusammen kauft, dann wird der Laptop mit der Tasche zusammen ein neuer Bündel-Artikel. Anmerkung: BOM = Stückliste", "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Fassen Sie eine Gruppe von Artikeln zu einem neuen Artikel zusammen. Dies ist nützlich, wenn Sie bestimmte Artikel zu einem Paket bündeln und einen Bestand an Artikel-Bündeln erhalten und nicht einen Bestand der einzelnen Artikel. Das Artikel-Bündel erhält für das Attribut ""Ist Lagerartikel"" den Wert ""Nein"" und für das Attribut ""Ist Verkaufsartikel"" den Wert ""Ja"". Beispiel: Wenn Sie Laptops und Tragetaschen getrennt verkaufen und einen bestimmten Preis anbieten, wenn der Kunde beides zusammen kauft, dann wird der Laptop mit der Tasche zusammen ein neuer Bündel-Artikel. Anmerkung: BOM = Stückliste",
Parent Item,Übergeordneter Artikel, Parent Item,Übergeordneter Artikel,
List items that form the package.,"Die Artikel auflisten, die das Paket bilden.", List items that form the package.,"Die Artikel auflisten, die das Paket bilden.",
SAL-QTN-.YYYY.-,SAL-QTN-.YYYY.-,
Quotation To,Angebot für, Quotation To,Angebot für,
Rate at which customer's currency is converted to company's base currency,"Kurs, zu dem die Währung des Kunden in die Basiswährung des Unternehmens umgerechnet wird", Rate at which customer's currency is converted to company's base currency,"Kurs, zu dem die Währung des Kunden in die Basiswährung des Unternehmens umgerechnet wird",
Rate at which Price list currency is converted to company's base currency,"Kurs, zu dem die Währung der Preisliste in die Basiswährung des Unternehmens umgerechnet wird", Rate at which Price list currency is converted to company's base currency,"Kurs, zu dem die Währung der Preisliste in die Basiswährung des Unternehmens umgerechnet wird",
@ -7704,7 +7702,6 @@ Quotation Item,Angebotsposition,
Against Doctype,Zu DocType, Against Doctype,Zu DocType,
Against Docname,Zu Dokumentenname, Against Docname,Zu Dokumentenname,
Additional Notes,Zusätzliche Bemerkungen, Additional Notes,Zusätzliche Bemerkungen,
SAL-ORD-.YYYY.-,SAL-ORD-.YYYY.-,
Skip Delivery Note,Lieferschein überspringen, Skip Delivery Note,Lieferschein überspringen,
In Words will be visible once you save the Sales Order.,"""In Worten"" wird sichtbar, sobald Sie den Auftrag speichern.", In Words will be visible once you save the Sales Order.,"""In Worten"" wird sichtbar, sobald Sie den Auftrag speichern.",
Track this Sales Order against any Project,Diesen Auftrag in jedem Projekt nachverfolgen, Track this Sales Order against any Project,Diesen Auftrag in jedem Projekt nachverfolgen,
@ -7935,7 +7932,7 @@ For reference,Zu Referenzzwecken,
Territory Targets,Ziele für die Region, Territory Targets,Ziele für die Region,
Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.,Artikelgruppenbezogene Budgets für diese Region erstellen. Durch Setzen der Auslieferungseinstellungen können auch saisonale Aspekte mit einbezogen werden., Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.,Artikelgruppenbezogene Budgets für diese Region erstellen. Durch Setzen der Auslieferungseinstellungen können auch saisonale Aspekte mit einbezogen werden.,
UOM Name,Maßeinheit-Name, UOM Name,Maßeinheit-Name,
Check this to disallow fractions. (for Nos),"Hier aktivieren, um keine Bruchteile zuzulassen (für Nr.)", Check this to disallow fractions. (for Nos),"Hier aktivieren, um keine Bruchteile zuzulassen (für Anzahl)",
Website Item Group,Webseiten-Artikelgruppe, Website Item Group,Webseiten-Artikelgruppe,
Cross Listing of Item in multiple groups,Kreuzweise Auflistung des Artikels in mehreren Gruppen, Cross Listing of Item in multiple groups,Kreuzweise Auflistung des Artikels in mehreren Gruppen,
Default settings for Shopping Cart,Standardeinstellungen für den Warenkorb, Default settings for Shopping Cart,Standardeinstellungen für den Warenkorb,
@ -8016,7 +8013,6 @@ Contact Information,Kontaktinformationen,
Email sent to,E-Mail versandt an, Email sent to,E-Mail versandt an,
Dispatch Information,Versandinformationen, Dispatch Information,Versandinformationen,
Estimated Arrival,Voraussichtliche Ankunft, Estimated Arrival,Voraussichtliche Ankunft,
MAT-DT-.YYYY.-,MAT-DT-.YYYY.-,
Initial Email Notification Sent,Erste E-Mail-Benachrichtigung gesendet, Initial Email Notification Sent,Erste E-Mail-Benachrichtigung gesendet,
Delivery Details,Lieferdetails, Delivery Details,Lieferdetails,
Driver Email,Fahrer-E-Mail, Driver Email,Fahrer-E-Mail,
@ -8176,7 +8172,6 @@ Purchase Receipt Item,Kaufbeleg-Artikel,
Landed Cost Purchase Receipt,Einstandspreis-Kaufbeleg, Landed Cost Purchase Receipt,Einstandspreis-Kaufbeleg,
Landed Cost Taxes and Charges,Einstandspreis Steuern und Gebühren, Landed Cost Taxes and Charges,Einstandspreis Steuern und Gebühren,
Landed Cost Voucher,Beleg über Einstandskosten, Landed Cost Voucher,Beleg über Einstandskosten,
MAT-LCV-.YYYY.-,MAT-LCV-.YYYY.-,
Purchase Receipts,Kaufbelege, Purchase Receipts,Kaufbelege,
Purchase Receipt Items,Kaufbeleg-Artikel, Purchase Receipt Items,Kaufbeleg-Artikel,
Get Items From Purchase Receipts,Artikel vom Kaufbeleg übernehmen, Get Items From Purchase Receipts,Artikel vom Kaufbeleg übernehmen,
@ -8184,7 +8179,6 @@ Distribute Charges Based On,Kosten auf folgender Grundlage verteilen,
Landed Cost Help,Hilfe zum Einstandpreis, Landed Cost Help,Hilfe zum Einstandpreis,
Manufacturers used in Items,Hersteller im Artikel verwendet, Manufacturers used in Items,Hersteller im Artikel verwendet,
Limited to 12 characters,Limitiert auf 12 Zeichen, Limited to 12 characters,Limitiert auf 12 Zeichen,
MAT-MR-.YYYY.-,MAT-MR-.YYYY.-,
Partially Ordered,Teilweise bestellt, Partially Ordered,Teilweise bestellt,
Transferred,Übergeben, Transferred,Übergeben,
% Ordered,% bestellt, % Ordered,% bestellt,
@ -8199,7 +8193,6 @@ Prevdoc DocType,Prevdoc DocType,
Parent Detail docname,Übergeordnetes Detail Dokumentenname, Parent Detail docname,Übergeordnetes Detail Dokumentenname,
"Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.","Packzettel für zu liefernde Pakete generieren. Wird verwendet, um Paketnummer, Packungsinhalt und das Gewicht zu dokumentieren.", "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.","Packzettel für zu liefernde Pakete generieren. Wird verwendet, um Paketnummer, Packungsinhalt und das Gewicht zu dokumentieren.",
Indicates that the package is a part of this delivery (Only Draft),"Zeigt an, dass das Paket ein Teil dieser Lieferung ist (nur Entwurf)", Indicates that the package is a part of this delivery (Only Draft),"Zeigt an, dass das Paket ein Teil dieser Lieferung ist (nur Entwurf)",
MAT-PAC-.YYYY.-,MAT-PAC-.YYYY.-,
From Package No.,Von Paket Nr., From Package No.,Von Paket Nr.,
Identification of the package for the delivery (for print),Kennzeichnung des Paketes für die Lieferung (für den Druck), Identification of the package for the delivery (for print),Kennzeichnung des Paketes für die Lieferung (für den Druck),
To Package No.,Bis Paket Nr., To Package No.,Bis Paket Nr.,
@ -8290,7 +8283,6 @@ Under AMC,Innerhalb des jährlichen Wartungsvertrags,
Out of AMC,Außerhalb des jährlichen Wartungsvertrags, Out of AMC,Außerhalb des jährlichen Wartungsvertrags,
Warranty Period (Days),Garantiefrist (Tage), Warranty Period (Days),Garantiefrist (Tage),
Serial No Details,Details zur Seriennummer, Serial No Details,Details zur Seriennummer,
MAT-STE-.YYYY.-,MAT-STE-.JJJJ.-,
Stock Entry Type,Bestandsbuchungsart, Stock Entry Type,Bestandsbuchungsart,
Stock Entry (Outward GIT),Bestandsbuchung (Outward GIT), Stock Entry (Outward GIT),Bestandsbuchung (Outward GIT),
Material Consumption for Manufacture,Materialverbrauch für die Herstellung, Material Consumption for Manufacture,Materialverbrauch für die Herstellung,
@ -8336,7 +8328,6 @@ Stock Queue (FIFO),Lagerverfahren (FIFO),
Is Cancelled,Ist storniert, Is Cancelled,Ist storniert,
Stock Reconciliation,Bestandsabgleich, Stock Reconciliation,Bestandsabgleich,
This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.,"Dieses Werkzeug hilft Ihnen dabei, die Menge und die Bewertung von Bestand im System zu aktualisieren oder zu ändern. Es wird in der Regel verwendet, um die Systemwerte und den aktuellen Bestand Ihrer Lager zu synchronisieren.", This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.,"Dieses Werkzeug hilft Ihnen dabei, die Menge und die Bewertung von Bestand im System zu aktualisieren oder zu ändern. Es wird in der Regel verwendet, um die Systemwerte und den aktuellen Bestand Ihrer Lager zu synchronisieren.",
MAT-RECO-.YYYY.-,MAT-RECO-.YYYY.-,
Reconciliation JSON,Abgleich JSON (JavaScript Object Notation), Reconciliation JSON,Abgleich JSON (JavaScript Object Notation),
Stock Reconciliation Item,Bestandsabgleich-Artikel, Stock Reconciliation Item,Bestandsabgleich-Artikel,
Before reconciliation,Vor Ausgleich, Before reconciliation,Vor Ausgleich,
@ -8796,8 +8787,7 @@ Availed ITC State/UT Tax,Verfügbare ITC State / UT Tax,
Availed ITC Cess,ITC Cess verfügbar, Availed ITC Cess,ITC Cess verfügbar,
Is Nil Rated or Exempted,Ist gleich Null oder ausgenommen, Is Nil Rated or Exempted,Ist gleich Null oder ausgenommen,
Is Non GST,Ist nicht GST, Is Non GST,Ist nicht GST,
ACC-SINV-RET-.YYYY.-,ACC-SINV-RET-.YYYY.-, E-Way Bill No.,E-Way Bill Nr.,
E-Way Bill No.,E-Way Bill No.,
Is Consolidated,Ist konsolidiert, Is Consolidated,Ist konsolidiert,
Billing Address GSTIN,Rechnungsadresse GSTIN, Billing Address GSTIN,Rechnungsadresse GSTIN,
Customer GSTIN,Kunde GSTIN, Customer GSTIN,Kunde GSTIN,
@ -9216,7 +9206,7 @@ Id,Ich würde,
Time Required (In Mins),Erforderliche Zeit (in Minuten), Time Required (In Mins),Erforderliche Zeit (in Minuten),
From Posting Date,Ab dem Buchungsdatum, From Posting Date,Ab dem Buchungsdatum,
To Posting Date,Zum Buchungsdatum, To Posting Date,Zum Buchungsdatum,
No records found,Keine Aufzeichnungen gefunden, No records found,Keine Einträge gefunden,
Customer/Lead Name,Name des Kunden / Lead, Customer/Lead Name,Name des Kunden / Lead,
Unmarked Days,Nicht markierte Tage, Unmarked Days,Nicht markierte Tage,
Jan,Jan., Jan,Jan.,
@ -9275,7 +9265,7 @@ Delay (in Days),Verzögerung (in Tagen),
Group by Sales Order,Nach Auftrag gruppieren, Group by Sales Order,Nach Auftrag gruppieren,
Sales Value,Verkaufswert, Sales Value,Verkaufswert,
Stock Qty vs Serial No Count,Lagermenge vs Seriennummer, Stock Qty vs Serial No Count,Lagermenge vs Seriennummer,
Serial No Count,Seriennummer nicht gezählt, Serial No Count,Seriennummern gezählt,
Work Order Summary,Arbeitsauftragsübersicht, Work Order Summary,Arbeitsauftragsübersicht,
Produce Qty,Menge produzieren, Produce Qty,Menge produzieren,
Lead Time (in mins),Vorlaufzeit (in Minuten), Lead Time (in mins),Vorlaufzeit (in Minuten),
@ -9569,7 +9559,7 @@ Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atl
You can alternatively disable selling price validation in {} to bypass this validation.,"Alternativ können Sie die Validierung des Verkaufspreises in {} deaktivieren, um diese Validierung zu umgehen.", You can alternatively disable selling price validation in {} to bypass this validation.,"Alternativ können Sie die Validierung des Verkaufspreises in {} deaktivieren, um diese Validierung zu umgehen.",
Invalid Selling Price,Ungültiger Verkaufspreis, Invalid Selling Price,Ungültiger Verkaufspreis,
Address needs to be linked to a Company. Please add a row for Company in the Links table.,Die Adresse muss mit einem Unternehmen verknüpft sein. Bitte fügen Sie eine Zeile für Firma in die Tabelle Links ein., Address needs to be linked to a Company. Please add a row for Company in the Links table.,Die Adresse muss mit einem Unternehmen verknüpft sein. Bitte fügen Sie eine Zeile für Firma in die Tabelle Links ein.,
Company Not Linked,Firma nicht verbunden, Company Not Linked,Firma nicht verknüpft,
Import Chart of Accounts from CSV / Excel files,Kontenplan aus CSV / Excel-Dateien importieren, Import Chart of Accounts from CSV / Excel files,Kontenplan aus CSV / Excel-Dateien importieren,
Completed Qty cannot be greater than 'Qty to Manufacture',Die abgeschlossene Menge darf nicht größer sein als die Menge bis zur Herstellung., Completed Qty cannot be greater than 'Qty to Manufacture',Die abgeschlossene Menge darf nicht größer sein als die Menge bis zur Herstellung.,
"Row {0}: For Supplier {1}, Email Address is Required to send an email","Zeile {0}: Für Lieferant {1} ist eine E-Mail-Adresse erforderlich, um eine E-Mail zu senden", "Row {0}: For Supplier {1}, Email Address is Required to send an email","Zeile {0}: Für Lieferant {1} ist eine E-Mail-Adresse erforderlich, um eine E-Mail zu senden",
@ -9656,7 +9646,7 @@ Hide Customer's Tax ID from Sales Transactions,Steuer-ID des Kunden vor Verkaufs
Action If Quality Inspection Is Not Submitted,Maßnahme Wenn keine Qualitätsprüfung eingereicht wird, Action If Quality Inspection Is Not Submitted,Maßnahme Wenn keine Qualitätsprüfung eingereicht wird,
Auto Insert Price List Rate If Missing,"Preisliste automatisch einfügen, falls fehlt", Auto Insert Price List Rate If Missing,"Preisliste automatisch einfügen, falls fehlt",
Automatically Set Serial Nos Based on FIFO,Seriennummern basierend auf FIFO automatisch einstellen, Automatically Set Serial Nos Based on FIFO,Seriennummern basierend auf FIFO automatisch einstellen,
Set Qty in Transactions Based on Serial No Input,Stellen Sie die Menge in Transaktionen basierend auf Seriennummer ohne Eingabe ein, Set Qty in Transactions Based on Serial No Input,Setze die Anzahl in der Transaktion basierend auf den Seriennummern,
Raise Material Request When Stock Reaches Re-order Level,"Erhöhen Sie die Materialanforderung, wenn der Lagerbestand die Nachbestellmenge erreicht", Raise Material Request When Stock Reaches Re-order Level,"Erhöhen Sie die Materialanforderung, wenn der Lagerbestand die Nachbestellmenge erreicht",
Notify by Email on Creation of Automatic Material Request,Benachrichtigen Sie per E-Mail über die Erstellung einer automatischen Materialanforderung, Notify by Email on Creation of Automatic Material Request,Benachrichtigen Sie per E-Mail über die Erstellung einer automatischen Materialanforderung,
Allow Material Transfer from Delivery Note to Sales Invoice,Materialübertragung vom Lieferschein zur Ausgangsrechnung zulassen, Allow Material Transfer from Delivery Note to Sales Invoice,Materialübertragung vom Lieferschein zur Ausgangsrechnung zulassen,
@ -9765,7 +9755,7 @@ Open Form View,Öffnen Sie die Formularansicht,
POS invoice {0} created succesfully,POS-Rechnung {0} erfolgreich erstellt, POS invoice {0} created succesfully,POS-Rechnung {0} erfolgreich erstellt,
Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.,Lagermenge nicht ausreichend für Artikelcode: {0} unter Lager {1}. Verfügbare Menge {2}., Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.,Lagermenge nicht ausreichend für Artikelcode: {0} unter Lager {1}. Verfügbare Menge {2}.,
Serial No: {0} has already been transacted into another POS Invoice.,Seriennummer: {0} wurde bereits in eine andere POS-Rechnung übertragen., Serial No: {0} has already been transacted into another POS Invoice.,Seriennummer: {0} wurde bereits in eine andere POS-Rechnung übertragen.,
Balance Serial No,Balance Seriennr, Balance Serial No,Stand Seriennummern,
Warehouse: {0} does not belong to {1},Lager: {0} gehört nicht zu {1}, Warehouse: {0} does not belong to {1},Lager: {0} gehört nicht zu {1},
Please select batches for batched item {0},Bitte wählen Sie Chargen für Chargenartikel {0} aus, Please select batches for batched item {0},Bitte wählen Sie Chargen für Chargenartikel {0} aus,
Please select quantity on row {0},Bitte wählen Sie die Menge in Zeile {0}, Please select quantity on row {0},Bitte wählen Sie die Menge in Zeile {0},

Can't render this file because it is too large.