Merge branch 'develop' into fix-reserve-qty
This commit is contained in:
commit
199b8dd0e3
@ -165,17 +165,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
def check_conversion_rate(self):
|
|
||||||
default_currency = erpnext.get_company_currency(self.company)
|
|
||||||
if not default_currency:
|
|
||||||
throw(_("Please enter default currency in Company Master"))
|
|
||||||
if (
|
|
||||||
(self.currency == default_currency and flt(self.conversion_rate) != 1.00)
|
|
||||||
or not self.conversion_rate
|
|
||||||
or (self.currency != default_currency and flt(self.conversion_rate) == 1.00)
|
|
||||||
):
|
|
||||||
throw(_("Conversion rate cannot be 0 or 1"))
|
|
||||||
|
|
||||||
def validate_credit_to_acc(self):
|
def validate_credit_to_acc(self):
|
||||||
if not self.credit_to:
|
if not self.credit_to:
|
||||||
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||||
|
@ -114,6 +114,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.set_income_account_for_fixed_assets()
|
self.set_income_account_for_fixed_assets()
|
||||||
self.validate_item_cost_centers()
|
self.validate_item_cost_centers()
|
||||||
self.validate_income_account()
|
self.validate_income_account()
|
||||||
|
self.check_conversion_rate()
|
||||||
|
|
||||||
validate_inter_company_party(
|
validate_inter_company_party(
|
||||||
self.doctype, self.customer, self.company, self.inter_company_invoice_reference
|
self.doctype, self.customer, self.company, self.inter_company_invoice_reference
|
||||||
|
@ -1583,6 +1583,17 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(gle)
|
self.assertTrue(gle)
|
||||||
|
|
||||||
|
def test_invoice_exchange_rate(self):
|
||||||
|
si = create_sales_invoice(
|
||||||
|
customer="_Test Customer USD",
|
||||||
|
debit_to="_Test Receivable USD - _TC",
|
||||||
|
currency="USD",
|
||||||
|
conversion_rate=1,
|
||||||
|
do_not_save=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, si.save)
|
||||||
|
|
||||||
def test_invalid_currency(self):
|
def test_invalid_currency(self):
|
||||||
# Customer currency = USD
|
# Customer currency = USD
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ frappe.query_reports["Purchase Order Analysis"] = {
|
|||||||
for (let option of status){
|
for (let option of status){
|
||||||
options.push({
|
options.push({
|
||||||
"value": option,
|
"value": option,
|
||||||
|
"label": __(option),
|
||||||
"description": ""
|
"description": ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1848,6 +1848,17 @@ class AccountsController(TransactionBase):
|
|||||||
jv.save()
|
jv.save()
|
||||||
jv.submit()
|
jv.submit()
|
||||||
|
|
||||||
|
def check_conversion_rate(self):
|
||||||
|
default_currency = erpnext.get_company_currency(self.company)
|
||||||
|
if not default_currency:
|
||||||
|
throw(_("Please enter default currency in Company Master"))
|
||||||
|
if (
|
||||||
|
(self.currency == default_currency and flt(self.conversion_rate) != 1.00)
|
||||||
|
or not self.conversion_rate
|
||||||
|
or (self.currency != default_currency and flt(self.conversion_rate) == 1.00)
|
||||||
|
):
|
||||||
|
throw(_("Conversion rate cannot be 0 or 1"))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
def get_tax_rate(account_head):
|
||||||
|
@ -166,7 +166,7 @@ class StockController(AccountsController):
|
|||||||
"against": warehouse_account[sle.warehouse]["account"],
|
"against": warehouse_account[sle.warehouse]["account"],
|
||||||
"cost_center": item_row.cost_center,
|
"cost_center": item_row.cost_center,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
"credit": flt(sle.stock_value_difference, precision),
|
"debit": -1 * flt(sle.stock_value_difference, precision),
|
||||||
"project": item_row.get("project") or self.get("project"),
|
"project": item_row.get("project") or self.get("project"),
|
||||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||||
},
|
},
|
||||||
|
@ -445,6 +445,7 @@ class BOM(WebsiteGenerator):
|
|||||||
and self.is_active
|
and self.is_active
|
||||||
):
|
):
|
||||||
frappe.db.set(self, "is_default", 1)
|
frappe.db.set(self, "is_default", 1)
|
||||||
|
frappe.db.set_value("Item", self.item, "default_bom", self.name)
|
||||||
else:
|
else:
|
||||||
frappe.db.set(self, "is_default", 0)
|
frappe.db.set(self, "is_default", 0)
|
||||||
item = frappe.get_doc("Item", self.item)
|
item = frappe.get_doc("Item", self.item)
|
||||||
|
@ -559,6 +559,42 @@ class TestBOM(FrappeTestCase):
|
|||||||
bom.submit()
|
bom.submit()
|
||||||
self.assertEqual(bom.items[0].rate, 42)
|
self.assertEqual(bom.items[0].rate, 42)
|
||||||
|
|
||||||
|
def test_set_default_bom_for_item_having_single_bom(self):
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
fg_item = make_item(properties={"is_stock_item": 1})
|
||||||
|
bom_item = make_item(properties={"is_stock_item": 1})
|
||||||
|
|
||||||
|
# Step 1: Create BOM
|
||||||
|
bom = frappe.new_doc("BOM")
|
||||||
|
bom.item = fg_item.item_code
|
||||||
|
bom.quantity = 1
|
||||||
|
bom.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": bom_item.item_code,
|
||||||
|
"qty": 1,
|
||||||
|
"uom": bom_item.stock_uom,
|
||||||
|
"stock_uom": bom_item.stock_uom,
|
||||||
|
"rate": 100.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
bom.save()
|
||||||
|
bom.submit()
|
||||||
|
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
|
||||||
|
|
||||||
|
# Step 2: Uncheck is_active field
|
||||||
|
bom.is_active = 0
|
||||||
|
bom.save()
|
||||||
|
bom.reload()
|
||||||
|
self.assertIsNone(frappe.get_value("Item", fg_item.item_code, "default_bom"))
|
||||||
|
|
||||||
|
# Step 3: Check is_active field
|
||||||
|
bom.is_active = 1
|
||||||
|
bom.save()
|
||||||
|
bom.reload()
|
||||||
|
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
|
||||||
|
|
||||||
|
|
||||||
def get_default_bom(item_code="_Test FG Item 2"):
|
def get_default_bom(item_code="_Test FG Item 2"):
|
||||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"current_bom",
|
|
||||||
"new_bom",
|
|
||||||
"column_break_3",
|
|
||||||
"update_type",
|
"update_type",
|
||||||
"status",
|
"status",
|
||||||
|
"column_break_3",
|
||||||
|
"current_bom",
|
||||||
|
"new_bom",
|
||||||
"error_log",
|
"error_log",
|
||||||
"progress_section",
|
"progress_section",
|
||||||
"current_level",
|
"current_level",
|
||||||
@ -37,6 +37,7 @@
|
|||||||
"options": "BOM"
|
"options": "BOM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.update_type === \"Replace BOM\"",
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
@ -87,6 +88,7 @@
|
|||||||
"options": "BOM Update Batch"
|
"options": "BOM Update Batch"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.status !== \"Completed\"",
|
||||||
"fieldname": "current_level",
|
"fieldname": "current_level",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"label": "Current Level"
|
"label": "Current Level"
|
||||||
@ -96,7 +98,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-06-06 15:15:23.883251",
|
"modified": "2022-06-20 15:43:55.696388",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Update Log",
|
"name": "BOM Update Log",
|
||||||
|
@ -6,6 +6,8 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder import DocType, Interval
|
||||||
|
from frappe.query_builder.functions import Now
|
||||||
from frappe.utils import cint, cstr
|
from frappe.utils import cint, cstr
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils import (
|
from erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils import (
|
||||||
@ -22,6 +24,17 @@ class BOMMissingError(frappe.ValidationError):
|
|||||||
|
|
||||||
|
|
||||||
class BOMUpdateLog(Document):
|
class BOMUpdateLog(Document):
|
||||||
|
@staticmethod
|
||||||
|
def clear_old_logs(days=None):
|
||||||
|
days = days or 90
|
||||||
|
table = DocType("BOM Update Log")
|
||||||
|
frappe.db.delete(
|
||||||
|
table,
|
||||||
|
filters=(
|
||||||
|
(table.modified < (Now() - Interval(days=days))) & (table.update_type == "Update Cost")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.update_type == "Replace BOM":
|
if self.update_type == "Replace BOM":
|
||||||
self.validate_boms_are_specified()
|
self.validate_boms_are_specified()
|
||||||
@ -77,7 +90,11 @@ class BOMUpdateLog(Document):
|
|||||||
now=frappe.flags.in_test,
|
now=frappe.flags.in_test,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
process_boms_cost_level_wise(self)
|
frappe.enqueue(
|
||||||
|
method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
|
||||||
|
update_doc=self,
|
||||||
|
now=frappe.flags.in_test,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_replace_bom_job(
|
def run_replace_bom_job(
|
||||||
@ -112,28 +129,31 @@ def process_boms_cost_level_wise(
|
|||||||
current_boms = {}
|
current_boms = {}
|
||||||
values = {}
|
values = {}
|
||||||
|
|
||||||
if update_doc.status == "Queued":
|
try:
|
||||||
# First level yet to process. On Submit.
|
if update_doc.status == "Queued":
|
||||||
current_level = 0
|
# First level yet to process. On Submit.
|
||||||
current_boms = get_leaf_boms()
|
current_level = 0
|
||||||
values = {
|
current_boms = get_leaf_boms()
|
||||||
"processed_boms": json.dumps({}),
|
values = {
|
||||||
"status": "In Progress",
|
"processed_boms": json.dumps({}),
|
||||||
"current_level": current_level,
|
"status": "In Progress",
|
||||||
}
|
"current_level": current_level,
|
||||||
else:
|
}
|
||||||
# Resume next level. via Cron Job.
|
else:
|
||||||
if not parent_boms:
|
# Resume next level. via Cron Job.
|
||||||
return
|
if not parent_boms:
|
||||||
|
return
|
||||||
|
|
||||||
current_level = cint(update_doc.current_level) + 1
|
current_level = cint(update_doc.current_level) + 1
|
||||||
|
|
||||||
# Process the next level BOMs. Stage parents as current BOMs.
|
# Process the next level BOMs. Stage parents as current BOMs.
|
||||||
current_boms = parent_boms.copy()
|
current_boms = parent_boms.copy()
|
||||||
values = {"current_level": current_level}
|
values = {"current_level": current_level}
|
||||||
|
|
||||||
set_values_in_log(update_doc.name, values, commit=True)
|
set_values_in_log(update_doc.name, values, commit=True)
|
||||||
queue_bom_cost_jobs(current_boms, update_doc, current_level)
|
queue_bom_cost_jobs(current_boms, update_doc, current_level)
|
||||||
|
except Exception:
|
||||||
|
handle_exception(update_doc)
|
||||||
|
|
||||||
|
|
||||||
def queue_bom_cost_jobs(
|
def queue_bom_cost_jobs(
|
||||||
@ -199,16 +219,22 @@ def resume_bom_cost_update_jobs():
|
|||||||
current_boms, processed_boms = get_processed_current_boms(log, bom_batches)
|
current_boms, processed_boms = get_processed_current_boms(log, bom_batches)
|
||||||
parent_boms = get_next_higher_level_boms(child_boms=current_boms, processed_boms=processed_boms)
|
parent_boms = get_next_higher_level_boms(child_boms=current_boms, processed_boms=processed_boms)
|
||||||
|
|
||||||
# Unset processed BOMs if log is complete, it is used for next level BOMs
|
# Unset processed BOMs (it is used for next level BOMs) & change status if log is complete
|
||||||
|
status = "Completed" if not parent_boms else "In Progress"
|
||||||
|
processed_boms = json.dumps([] if not parent_boms else processed_boms)
|
||||||
set_values_in_log(
|
set_values_in_log(
|
||||||
log.name,
|
log.name,
|
||||||
values={
|
values={
|
||||||
"processed_boms": json.dumps([] if not parent_boms else processed_boms),
|
"processed_boms": processed_boms,
|
||||||
"status": "Completed" if not parent_boms else "In Progress",
|
"status": status,
|
||||||
},
|
},
|
||||||
commit=True,
|
commit=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# clear progress section
|
||||||
|
if status == "Completed":
|
||||||
|
frappe.db.delete("BOM Update Batch", {"parent": log.name})
|
||||||
|
|
||||||
if parent_boms: # there is a next level to process
|
if parent_boms: # there is a next level to process
|
||||||
process_boms_cost_level_wise(
|
process_boms_cost_level_wise(
|
||||||
update_doc=frappe.get_doc("BOM Update Log", log.name), parent_boms=parent_boms
|
update_doc=frappe.get_doc("BOM Update Log", log.name), parent_boms=parent_boms
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
frappe.listview_settings['BOM Update Log'] = {
|
frappe.listview_settings['BOM Update Log'] = {
|
||||||
add_fields: ["status"],
|
add_fields: ["status"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: (doc) => {
|
||||||
let status_map = {
|
let status_map = {
|
||||||
"Queued": "orange",
|
"Queued": "orange",
|
||||||
"In Progress": "blue",
|
"In Progress": "blue",
|
||||||
@ -9,5 +9,22 @@ frappe.listview_settings['BOM Update Log'] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return [__(doc.status), status_map[doc.status], "status,=," + doc.status];
|
return [__(doc.status), status_map[doc.status], "status,=," + doc.status];
|
||||||
}
|
},
|
||||||
|
onload: () => {
|
||||||
|
if (!frappe.model.can_write("Log Settings")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sidebar_entry = $(
|
||||||
|
'<ul class="list-unstyled sidebar-menu log-retention-note"></ul>'
|
||||||
|
).appendTo(cur_list.page.sidebar);
|
||||||
|
let message = __("Note: Automatic log deletion only applies to logs of type <i>Update Cost</i>");
|
||||||
|
$(`<hr><div class='text-muted'>${message}</div>`).appendTo(sidebar_entry);
|
||||||
|
|
||||||
|
frappe.require("logtypes.bundle.js", () => {
|
||||||
|
frappe.utils.logtypes.show_log_retention_message(cur_list.doctype);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
};
|
};
|
@ -1,6 +1,8 @@
|
|||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.tests.utils import FrappeTestCase, change_settings, timeout
|
from frappe.tests.utils import FrappeTestCase, change_settings, timeout
|
||||||
from frappe.utils import add_days, add_months, cint, flt, now, today
|
from frappe.utils import add_days, add_months, cint, flt, now, today
|
||||||
@ -19,6 +21,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import (
|
|||||||
)
|
)
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
from erpnext.stock.utils import get_bin
|
from erpnext.stock.utils import get_bin
|
||||||
@ -28,6 +31,7 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.warehouse = "_Test Warehouse 2 - _TC"
|
self.warehouse = "_Test Warehouse 2 - _TC"
|
||||||
self.item = "_Test Item"
|
self.item = "_Test Item"
|
||||||
|
prepare_data_for_backflush_based_on_materials_transferred()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
@ -527,6 +531,8 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
work_order.cancel()
|
work_order.cancel()
|
||||||
|
|
||||||
def test_work_order_with_non_transfer_item(self):
|
def test_work_order_with_non_transfer_item(self):
|
||||||
|
frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
|
||||||
|
|
||||||
items = {"Finished Good Transfer Item": 1, "_Test FG Item": 1, "_Test FG Item 1": 0}
|
items = {"Finished Good Transfer Item": 1, "_Test FG Item": 1, "_Test FG Item 1": 0}
|
||||||
for item, allow_transfer in items.items():
|
for item, allow_transfer in items.items():
|
||||||
make_item(item, {"include_item_in_manufacturing": allow_transfer})
|
make_item(item, {"include_item_in_manufacturing": allow_transfer})
|
||||||
@ -1071,7 +1077,7 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
sm = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 100))
|
sm = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 100))
|
||||||
for row in sm.get("items"):
|
for row in sm.get("items"):
|
||||||
if row.get("item_code") == "_Test Item":
|
if row.get("item_code") == "_Test Item":
|
||||||
row.qty = 110
|
row.qty = 120
|
||||||
|
|
||||||
sm.submit()
|
sm.submit()
|
||||||
cancel_stock_entry.append(sm.name)
|
cancel_stock_entry.append(sm.name)
|
||||||
@ -1079,21 +1085,21 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 90))
|
s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 90))
|
||||||
for row in s.get("items"):
|
for row in s.get("items"):
|
||||||
if row.get("item_code") == "_Test Item":
|
if row.get("item_code") == "_Test Item":
|
||||||
self.assertEqual(row.get("qty"), 100)
|
self.assertEqual(row.get("qty"), 108)
|
||||||
s.submit()
|
s.submit()
|
||||||
cancel_stock_entry.append(s.name)
|
cancel_stock_entry.append(s.name)
|
||||||
|
|
||||||
s1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
|
s1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
|
||||||
for row in s1.get("items"):
|
for row in s1.get("items"):
|
||||||
if row.get("item_code") == "_Test Item":
|
if row.get("item_code") == "_Test Item":
|
||||||
self.assertEqual(row.get("qty"), 5)
|
self.assertEqual(row.get("qty"), 6)
|
||||||
s1.submit()
|
s1.submit()
|
||||||
cancel_stock_entry.append(s1.name)
|
cancel_stock_entry.append(s1.name)
|
||||||
|
|
||||||
s2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
|
s2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
|
||||||
for row in s2.get("items"):
|
for row in s2.get("items"):
|
||||||
if row.get("item_code") == "_Test Item":
|
if row.get("item_code") == "_Test Item":
|
||||||
self.assertEqual(row.get("qty"), 5)
|
self.assertEqual(row.get("qty"), 6)
|
||||||
|
|
||||||
cancel_stock_entry.reverse()
|
cancel_stock_entry.reverse()
|
||||||
for ste in cancel_stock_entry:
|
for ste in cancel_stock_entry:
|
||||||
@ -1203,6 +1209,269 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
self.assertEqual(work_order.required_items[0].transferred_qty, 1)
|
self.assertEqual(work_order.required_items[0].transferred_qty, 1)
|
||||||
self.assertEqual(work_order.required_items[1].transferred_qty, 2)
|
self.assertEqual(work_order.required_items[1].transferred_qty, 2)
|
||||||
|
|
||||||
|
def test_backflushed_batch_raw_materials_based_on_transferred(self):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Manufacturing Settings",
|
||||||
|
None,
|
||||||
|
"backflush_raw_materials_based_on",
|
||||||
|
"Material Transferred for Manufacture",
|
||||||
|
)
|
||||||
|
|
||||||
|
batch_item = "Test Batch MCC Keyboard"
|
||||||
|
fg_item = "Test FG Item with Batch Raw Materials"
|
||||||
|
|
||||||
|
ste_doc = test_stock_entry.make_stock_entry(
|
||||||
|
item_code=batch_item, target="Stores - _TC", qty=2, basic_rate=100, do_not_save=True
|
||||||
|
)
|
||||||
|
|
||||||
|
ste_doc.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": batch_item,
|
||||||
|
"item_name": batch_item,
|
||||||
|
"description": batch_item,
|
||||||
|
"basic_rate": 100,
|
||||||
|
"t_warehouse": "Stores - _TC",
|
||||||
|
"qty": 2,
|
||||||
|
"uom": "Nos",
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
"conversion_factor": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Inward raw materials in Stores warehouse
|
||||||
|
ste_doc.insert()
|
||||||
|
ste_doc.submit()
|
||||||
|
|
||||||
|
batch_list = [row.batch_no for row in ste_doc.items]
|
||||||
|
|
||||||
|
wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4)
|
||||||
|
transferred_ste_doc = frappe.get_doc(
|
||||||
|
make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
transferred_ste_doc.items[0].qty = 2
|
||||||
|
transferred_ste_doc.items[0].batch_no = batch_list[0]
|
||||||
|
|
||||||
|
new_row = copy.deepcopy(transferred_ste_doc.items[0])
|
||||||
|
new_row.name = ""
|
||||||
|
new_row.batch_no = batch_list[1]
|
||||||
|
|
||||||
|
# Transferred two batches from Stores to WIP Warehouse
|
||||||
|
transferred_ste_doc.append("items", new_row)
|
||||||
|
transferred_ste_doc.submit()
|
||||||
|
|
||||||
|
# First Manufacture stock entry
|
||||||
|
manufacture_ste_doc1 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 1))
|
||||||
|
|
||||||
|
# Batch no should be same as transferred Batch no
|
||||||
|
self.assertEqual(manufacture_ste_doc1.items[0].batch_no, batch_list[0])
|
||||||
|
self.assertEqual(manufacture_ste_doc1.items[0].qty, 1)
|
||||||
|
|
||||||
|
manufacture_ste_doc1.submit()
|
||||||
|
|
||||||
|
# Second Manufacture stock entry
|
||||||
|
manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 2))
|
||||||
|
|
||||||
|
# Batch no should be same as transferred Batch no
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[0].batch_no, batch_list[0])
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[0].qty, 1)
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[1].batch_no, batch_list[1])
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[1].qty, 1)
|
||||||
|
|
||||||
|
def test_backflushed_serial_no_raw_materials_based_on_transferred(self):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Manufacturing Settings",
|
||||||
|
None,
|
||||||
|
"backflush_raw_materials_based_on",
|
||||||
|
"Material Transferred for Manufacture",
|
||||||
|
)
|
||||||
|
|
||||||
|
sn_item = "Test Serial No BTT Headphone"
|
||||||
|
fg_item = "Test FG Item with Serial No Raw Materials"
|
||||||
|
|
||||||
|
ste_doc = test_stock_entry.make_stock_entry(
|
||||||
|
item_code=sn_item, target="Stores - _TC", qty=4, basic_rate=100, do_not_save=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Inward raw materials in Stores warehouse
|
||||||
|
ste_doc.submit()
|
||||||
|
|
||||||
|
serial_nos_list = sorted(get_serial_nos(ste_doc.items[0].serial_no))
|
||||||
|
|
||||||
|
wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4)
|
||||||
|
transferred_ste_doc = frappe.get_doc(
|
||||||
|
make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
transferred_ste_doc.items[0].serial_no = "\n".join(serial_nos_list)
|
||||||
|
transferred_ste_doc.submit()
|
||||||
|
|
||||||
|
# First Manufacture stock entry
|
||||||
|
manufacture_ste_doc1 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 1))
|
||||||
|
|
||||||
|
# Serial nos should be same as transferred Serial nos
|
||||||
|
self.assertEqual(get_serial_nos(manufacture_ste_doc1.items[0].serial_no), serial_nos_list[0:1])
|
||||||
|
self.assertEqual(manufacture_ste_doc1.items[0].qty, 1)
|
||||||
|
|
||||||
|
manufacture_ste_doc1.submit()
|
||||||
|
|
||||||
|
# Second Manufacture stock entry
|
||||||
|
manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 2))
|
||||||
|
|
||||||
|
# Serial nos should be same as transferred Serial nos
|
||||||
|
self.assertEqual(get_serial_nos(manufacture_ste_doc2.items[0].serial_no), serial_nos_list[1:3])
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[0].qty, 2)
|
||||||
|
|
||||||
|
def test_backflushed_serial_no_batch_raw_materials_based_on_transferred(self):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Manufacturing Settings",
|
||||||
|
None,
|
||||||
|
"backflush_raw_materials_based_on",
|
||||||
|
"Material Transferred for Manufacture",
|
||||||
|
)
|
||||||
|
|
||||||
|
sn_batch_item = "Test Batch Serial No WebCam"
|
||||||
|
fg_item = "Test FG Item with Serial & Batch No Raw Materials"
|
||||||
|
|
||||||
|
ste_doc = test_stock_entry.make_stock_entry(
|
||||||
|
item_code=sn_batch_item, target="Stores - _TC", qty=2, basic_rate=100, do_not_save=True
|
||||||
|
)
|
||||||
|
|
||||||
|
ste_doc.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": sn_batch_item,
|
||||||
|
"item_name": sn_batch_item,
|
||||||
|
"description": sn_batch_item,
|
||||||
|
"basic_rate": 100,
|
||||||
|
"t_warehouse": "Stores - _TC",
|
||||||
|
"qty": 2,
|
||||||
|
"uom": "Nos",
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
"conversion_factor": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Inward raw materials in Stores warehouse
|
||||||
|
ste_doc.insert()
|
||||||
|
ste_doc.submit()
|
||||||
|
|
||||||
|
batch_dict = {row.batch_no: get_serial_nos(row.serial_no) for row in ste_doc.items}
|
||||||
|
batches = list(batch_dict.keys())
|
||||||
|
|
||||||
|
wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4)
|
||||||
|
transferred_ste_doc = frappe.get_doc(
|
||||||
|
make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
transferred_ste_doc.items[0].qty = 2
|
||||||
|
transferred_ste_doc.items[0].batch_no = batches[0]
|
||||||
|
transferred_ste_doc.items[0].serial_no = "\n".join(batch_dict.get(batches[0]))
|
||||||
|
|
||||||
|
new_row = copy.deepcopy(transferred_ste_doc.items[0])
|
||||||
|
new_row.name = ""
|
||||||
|
new_row.batch_no = batches[1]
|
||||||
|
new_row.serial_no = "\n".join(batch_dict.get(batches[1]))
|
||||||
|
|
||||||
|
# Transferred two batches from Stores to WIP Warehouse
|
||||||
|
transferred_ste_doc.append("items", new_row)
|
||||||
|
transferred_ste_doc.submit()
|
||||||
|
|
||||||
|
# First Manufacture stock entry
|
||||||
|
manufacture_ste_doc1 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 1))
|
||||||
|
|
||||||
|
# Batch no & Serial Nos should be same as transferred Batch no & Serial Nos
|
||||||
|
batch_no = manufacture_ste_doc1.items[0].batch_no
|
||||||
|
self.assertEqual(
|
||||||
|
get_serial_nos(manufacture_ste_doc1.items[0].serial_no)[0], batch_dict.get(batch_no)[0]
|
||||||
|
)
|
||||||
|
self.assertEqual(manufacture_ste_doc1.items[0].qty, 1)
|
||||||
|
|
||||||
|
manufacture_ste_doc1.submit()
|
||||||
|
|
||||||
|
# Second Manufacture stock entry
|
||||||
|
manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 2))
|
||||||
|
|
||||||
|
# Batch no & Serial Nos should be same as transferred Batch no & Serial Nos
|
||||||
|
batch_no = manufacture_ste_doc2.items[0].batch_no
|
||||||
|
self.assertEqual(
|
||||||
|
get_serial_nos(manufacture_ste_doc2.items[0].serial_no)[0], batch_dict.get(batch_no)[1]
|
||||||
|
)
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[0].qty, 1)
|
||||||
|
|
||||||
|
batch_no = manufacture_ste_doc2.items[1].batch_no
|
||||||
|
self.assertEqual(
|
||||||
|
get_serial_nos(manufacture_ste_doc2.items[1].serial_no)[0], batch_dict.get(batch_no)[0]
|
||||||
|
)
|
||||||
|
self.assertEqual(manufacture_ste_doc2.items[1].qty, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_data_for_backflush_based_on_materials_transferred():
|
||||||
|
batch_item_doc = make_item(
|
||||||
|
"Test Batch MCC Keyboard",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"batch_number_series": "TBMK.#####",
|
||||||
|
"valuation_rate": 100,
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
item = make_item(
|
||||||
|
"Test FG Item with Batch Raw Materials",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
make_bom(item=item.name, source_warehouse="Stores - _TC", raw_materials=[batch_item_doc.name])
|
||||||
|
|
||||||
|
sn_item_doc = make_item(
|
||||||
|
"Test Serial No BTT Headphone",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_serial_no": 1,
|
||||||
|
"serial_no_series": "TSBH.#####",
|
||||||
|
"valuation_rate": 100,
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
item = make_item(
|
||||||
|
"Test FG Item with Serial No Raw Materials",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
make_bom(item=item.name, source_warehouse="Stores - _TC", raw_materials=[sn_item_doc.name])
|
||||||
|
|
||||||
|
sn_batch_item_doc = make_item(
|
||||||
|
"Test Batch Serial No WebCam",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"batch_number_series": "TBSW.#####",
|
||||||
|
"has_serial_no": 1,
|
||||||
|
"serial_no_series": "TBSWC.#####",
|
||||||
|
"valuation_rate": 100,
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
item = make_item(
|
||||||
|
"Test FG Item with Serial & Batch No Raw Materials",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
make_bom(item=item.name, source_warehouse="Stores - _TC", raw_materials=[sn_batch_item_doc.name])
|
||||||
|
|
||||||
|
|
||||||
def update_job_card(job_card, jc_qty=None):
|
def update_job_card(job_card, jc_qty=None):
|
||||||
employee = frappe.db.get_value("Employee", {"status": "Active"}, "name")
|
employee = frappe.db.get_value("Employee", {"status": "Active"}, "name")
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import qb
|
from frappe import qb
|
||||||
from frappe.query_builder import Case
|
from frappe.query_builder import Case, CustomFunction
|
||||||
from frappe.query_builder.custom import ConstantColumn
|
from frappe.query_builder.custom import ConstantColumn
|
||||||
from frappe.query_builder.functions import IfNull
|
from frappe.query_builder.functions import IfNull
|
||||||
|
|
||||||
@ -87,6 +87,7 @@ def execute():
|
|||||||
|
|
||||||
gl = qb.DocType("GL Entry")
|
gl = qb.DocType("GL Entry")
|
||||||
account = qb.DocType("Account")
|
account = qb.DocType("Account")
|
||||||
|
ifelse = CustomFunction("IF", ["condition", "then", "else"])
|
||||||
|
|
||||||
gl_entries = (
|
gl_entries = (
|
||||||
qb.from_(gl)
|
qb.from_(gl)
|
||||||
@ -96,8 +97,12 @@ def execute():
|
|||||||
gl.star,
|
gl.star,
|
||||||
ConstantColumn(1).as_("docstatus"),
|
ConstantColumn(1).as_("docstatus"),
|
||||||
account.account_type.as_("account_type"),
|
account.account_type.as_("account_type"),
|
||||||
IfNull(gl.against_voucher_type, gl.voucher_type).as_("against_voucher_type"),
|
IfNull(
|
||||||
IfNull(gl.against_voucher, gl.voucher_no).as_("against_voucher_no"),
|
ifelse(gl.against_voucher_type == "", None, gl.against_voucher_type), gl.voucher_type
|
||||||
|
).as_("against_voucher_type"),
|
||||||
|
IfNull(ifelse(gl.against_voucher == "", None, gl.against_voucher), gl.voucher_no).as_(
|
||||||
|
"against_voucher_no"
|
||||||
|
),
|
||||||
# convert debit/credit to amount
|
# convert debit/credit to amount
|
||||||
Case()
|
Case()
|
||||||
.when(account.account_type == "Receivable", gl.debit - gl.credit)
|
.when(account.account_type == "Receivable", gl.debit - gl.credit)
|
||||||
|
@ -375,6 +375,12 @@ def create_internal_customer(
|
|||||||
if not allowed_to_interact_with:
|
if not allowed_to_interact_with:
|
||||||
allowed_to_interact_with = represents_company
|
allowed_to_interact_with = represents_company
|
||||||
|
|
||||||
|
exisiting_representative = frappe.db.get_value(
|
||||||
|
"Customer", {"represents_company": represents_company}
|
||||||
|
)
|
||||||
|
if exisiting_representative:
|
||||||
|
return exisiting_representative
|
||||||
|
|
||||||
if not frappe.db.exists("Customer", customer_name):
|
if not frappe.db.exists("Customer", customer_name):
|
||||||
customer = frappe.get_doc(
|
customer = frappe.get_doc(
|
||||||
{
|
{
|
||||||
|
@ -55,6 +55,7 @@ frappe.query_reports["Sales Order Analysis"] = {
|
|||||||
for (let option of status){
|
for (let option of status){
|
||||||
options.push({
|
options.push({
|
||||||
"value": option,
|
"value": option,
|
||||||
|
"label": __(option),
|
||||||
"description": ""
|
"description": ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1064,6 +1064,33 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertEqual(dn.items[0].rate, rate)
|
self.assertEqual(dn.items[0].rate, rate)
|
||||||
|
|
||||||
|
def test_internal_transfer_precision_gle(self):
|
||||||
|
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||||
|
|
||||||
|
item = make_item(properties={"valuation_method": "Moving Average"}).name
|
||||||
|
company = "_Test Company with perpetual inventory"
|
||||||
|
warehouse = "Stores - TCP1"
|
||||||
|
target = "Finished Goods - TCP1"
|
||||||
|
customer = create_internal_customer(represents_company=company)
|
||||||
|
|
||||||
|
# average rate = 128.015
|
||||||
|
rates = [101.45, 150.46, 138.25, 121.9]
|
||||||
|
|
||||||
|
for rate in rates:
|
||||||
|
make_stock_entry(item_code=item, target=warehouse, qty=1, rate=rate)
|
||||||
|
|
||||||
|
dn = create_delivery_note(
|
||||||
|
item_code=item,
|
||||||
|
company=company,
|
||||||
|
customer=customer,
|
||||||
|
qty=4,
|
||||||
|
warehouse=warehouse,
|
||||||
|
target_warehouse=target,
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
frappe.db.exists("GL Entry", {"voucher_no": dn.name, "voucher_type": dn.doctype})
|
||||||
|
)
|
||||||
|
|
||||||
def test_reserve_qty_on_sales_return(self):
|
def test_reserve_qty_on_sales_return(self):
|
||||||
frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
|
frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
|
||||||
self.reserved_qty_check()
|
self.reserved_qty_check()
|
||||||
|
@ -596,21 +596,6 @@ class StockEntry(StockController):
|
|||||||
title=_("Insufficient Stock"),
|
title=_("Insufficient Stock"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_serial_nos(self, work_order):
|
|
||||||
previous_se = frappe.db.get_value(
|
|
||||||
"Stock Entry",
|
|
||||||
{"work_order": work_order, "purpose": "Material Transfer for Manufacture"},
|
|
||||||
"name",
|
|
||||||
)
|
|
||||||
|
|
||||||
for d in self.get("items"):
|
|
||||||
transferred_serial_no = frappe.db.get_value(
|
|
||||||
"Stock Entry Detail", {"parent": previous_se, "item_code": d.item_code}, "serial_no"
|
|
||||||
)
|
|
||||||
|
|
||||||
if transferred_serial_no:
|
|
||||||
d.serial_no = transferred_serial_no
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_stock_and_rate(self):
|
def get_stock_and_rate(self):
|
||||||
"""
|
"""
|
||||||
@ -1321,7 +1306,7 @@ class StockEntry(StockController):
|
|||||||
and not self.pro_doc.skip_transfer
|
and not self.pro_doc.skip_transfer
|
||||||
and self.flags.backflush_based_on == "Material Transferred for Manufacture"
|
and self.flags.backflush_based_on == "Material Transferred for Manufacture"
|
||||||
):
|
):
|
||||||
self.get_transfered_raw_materials()
|
self.add_transfered_raw_materials_in_items()
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
self.work_order
|
self.work_order
|
||||||
@ -1365,7 +1350,6 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
# fetch the serial_no of the first stock entry for the second stock entry
|
# fetch the serial_no of the first stock entry for the second stock entry
|
||||||
if self.work_order and self.purpose == "Manufacture":
|
if self.work_order and self.purpose == "Manufacture":
|
||||||
self.set_serial_nos(self.work_order)
|
|
||||||
work_order = frappe.get_doc("Work Order", self.work_order)
|
work_order = frappe.get_doc("Work Order", self.work_order)
|
||||||
add_additional_cost(self, work_order)
|
add_additional_cost(self, work_order)
|
||||||
|
|
||||||
@ -1655,119 +1639,78 @@ class StockEntry(StockController):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_transfered_raw_materials(self):
|
def add_transfered_raw_materials_in_items(self) -> None:
|
||||||
transferred_materials = frappe.db.sql(
|
available_materials = get_available_materials(self.work_order)
|
||||||
"""
|
|
||||||
select
|
wo_data = frappe.db.get_value(
|
||||||
item_name, original_item, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
"Work Order",
|
||||||
description, stock_uom, expense_account, cost_center
|
|
||||||
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
|
||||||
where
|
|
||||||
se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
|
|
||||||
and se.work_order= %s and ifnull(sed.t_warehouse, '') != ''
|
|
||||||
group by sed.item_code, sed.t_warehouse
|
|
||||||
""",
|
|
||||||
self.work_order,
|
self.work_order,
|
||||||
|
["qty", "produced_qty", "material_transferred_for_manufacturing as trans_qty"],
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
materials_already_backflushed = frappe.db.sql(
|
for key, row in available_materials.items():
|
||||||
"""
|
remaining_qty_to_produce = flt(wo_data.trans_qty) - flt(wo_data.produced_qty)
|
||||||
select
|
if remaining_qty_to_produce <= 0:
|
||||||
item_code, sed.s_warehouse as warehouse, sum(qty) as qty
|
continue
|
||||||
from
|
|
||||||
`tabStock Entry` se, `tabStock Entry Detail` sed
|
|
||||||
where
|
|
||||||
se.name = sed.parent and se.docstatus=1
|
|
||||||
and (se.purpose='Manufacture' or se.purpose='Material Consumption for Manufacture')
|
|
||||||
and se.work_order= %s and ifnull(sed.s_warehouse, '') != ''
|
|
||||||
group by sed.item_code, sed.s_warehouse
|
|
||||||
""",
|
|
||||||
self.work_order,
|
|
||||||
as_dict=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
backflushed_materials = {}
|
qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce
|
||||||
for d in materials_already_backflushed:
|
|
||||||
backflushed_materials.setdefault(d.item_code, []).append({d.warehouse: d.qty})
|
|
||||||
|
|
||||||
po_qty = frappe.db.sql(
|
|
||||||
"""select qty, produced_qty, material_transferred_for_manufacturing from
|
|
||||||
`tabWork Order` where name=%s""",
|
|
||||||
self.work_order,
|
|
||||||
as_dict=1,
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
manufacturing_qty = flt(po_qty.qty) or 1
|
|
||||||
produced_qty = flt(po_qty.produced_qty)
|
|
||||||
trans_qty = flt(po_qty.material_transferred_for_manufacturing) or 1
|
|
||||||
|
|
||||||
for item in transferred_materials:
|
|
||||||
qty = item.qty
|
|
||||||
item_code = item.original_item or item.item_code
|
|
||||||
req_items = frappe.get_all(
|
|
||||||
"Work Order Item",
|
|
||||||
filters={"parent": self.work_order, "item_code": item_code},
|
|
||||||
fields=["required_qty", "consumed_qty"],
|
|
||||||
)
|
|
||||||
|
|
||||||
req_qty = flt(req_items[0].required_qty) if req_items else flt(4)
|
|
||||||
req_qty_each = flt(req_qty / manufacturing_qty)
|
|
||||||
consumed_qty = flt(req_items[0].consumed_qty) if req_items else 0
|
|
||||||
|
|
||||||
if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
|
|
||||||
if qty >= req_qty:
|
|
||||||
qty = (req_qty / trans_qty) * flt(self.fg_completed_qty)
|
|
||||||
else:
|
|
||||||
qty = qty - consumed_qty
|
|
||||||
|
|
||||||
if self.purpose == "Manufacture":
|
|
||||||
# If Material Consumption is booked, must pull only remaining components to finish product
|
|
||||||
if consumed_qty != 0:
|
|
||||||
remaining_qty = consumed_qty - (produced_qty * req_qty_each)
|
|
||||||
exhaust_qty = req_qty_each * produced_qty
|
|
||||||
if remaining_qty > exhaust_qty:
|
|
||||||
if (remaining_qty / (req_qty_each * flt(self.fg_completed_qty))) >= 1:
|
|
||||||
qty = 0
|
|
||||||
else:
|
|
||||||
qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty
|
|
||||||
else:
|
|
||||||
if self.flags.backflush_based_on == "Material Transferred for Manufacture":
|
|
||||||
qty = (item.qty / trans_qty) * flt(self.fg_completed_qty)
|
|
||||||
else:
|
|
||||||
qty = req_qty_each * flt(self.fg_completed_qty)
|
|
||||||
|
|
||||||
elif backflushed_materials.get(item.item_code):
|
|
||||||
precision = frappe.get_precision("Stock Entry Detail", "qty")
|
|
||||||
for d in backflushed_materials.get(item.item_code):
|
|
||||||
if d.get(item.warehouse) > 0:
|
|
||||||
if qty > req_qty:
|
|
||||||
qty = (
|
|
||||||
(flt(qty, precision) - flt(d.get(item.warehouse), precision))
|
|
||||||
/ (flt(trans_qty, precision) - flt(produced_qty, precision))
|
|
||||||
) * flt(self.fg_completed_qty)
|
|
||||||
|
|
||||||
d[item.warehouse] -= qty
|
|
||||||
|
|
||||||
|
item = row.item_details
|
||||||
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
|
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
|
||||||
qty = frappe.utils.ceil(qty)
|
qty = frappe.utils.ceil(qty)
|
||||||
|
|
||||||
if qty > 0:
|
if row.batch_details:
|
||||||
self.add_to_stock_entry_detail(
|
for batch_no, batch_qty in row.batch_details.items():
|
||||||
{
|
if qty <= 0 or batch_qty <= 0:
|
||||||
item.item_code: {
|
continue
|
||||||
"from_warehouse": item.warehouse,
|
|
||||||
"to_warehouse": "",
|
if batch_qty > qty:
|
||||||
"qty": qty,
|
batch_qty = qty
|
||||||
"item_name": item.item_name,
|
|
||||||
"description": item.description,
|
item.batch_no = batch_no
|
||||||
"stock_uom": item.stock_uom,
|
self.update_item_in_stock_entry_detail(row, item, batch_qty)
|
||||||
"expense_account": item.expense_account,
|
|
||||||
"cost_center": item.buying_cost_center,
|
row.batch_details[batch_no] -= batch_qty
|
||||||
"original_item": item.original_item,
|
qty -= batch_qty
|
||||||
}
|
else:
|
||||||
}
|
self.update_item_in_stock_entry_detail(row, item, qty)
|
||||||
)
|
|
||||||
|
def update_item_in_stock_entry_detail(self, row, item, qty) -> None:
|
||||||
|
ste_item_details = {
|
||||||
|
"from_warehouse": item.warehouse,
|
||||||
|
"to_warehouse": "",
|
||||||
|
"qty": qty,
|
||||||
|
"item_name": item.item_name,
|
||||||
|
"batch_no": item.batch_no,
|
||||||
|
"description": item.description,
|
||||||
|
"stock_uom": item.stock_uom,
|
||||||
|
"expense_account": item.expense_account,
|
||||||
|
"cost_center": item.buying_cost_center,
|
||||||
|
"original_item": item.original_item,
|
||||||
|
}
|
||||||
|
|
||||||
|
if row.serial_nos:
|
||||||
|
serial_nos = row.serial_nos
|
||||||
|
if item.batch_no:
|
||||||
|
serial_nos = self.get_serial_nos_based_on_transferred_batch(item.batch_no, row.serial_nos)
|
||||||
|
|
||||||
|
serial_nos = serial_nos[0 : cint(qty)]
|
||||||
|
ste_item_details["serial_no"] = "\n".join(serial_nos)
|
||||||
|
|
||||||
|
# remove consumed serial nos from list
|
||||||
|
for sn in serial_nos:
|
||||||
|
row.serial_nos.remove(sn)
|
||||||
|
|
||||||
|
self.add_to_stock_entry_detail({item.item_code: ste_item_details})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_serial_nos_based_on_transferred_batch(batch_no, serial_nos) -> list:
|
||||||
|
serial_nos = frappe.get_all(
|
||||||
|
"Serial No", filters={"batch_no": batch_no, "name": ("in", serial_nos)}, order_by="creation"
|
||||||
|
)
|
||||||
|
|
||||||
|
return [d.name for d in serial_nos]
|
||||||
|
|
||||||
def get_pending_raw_materials(self, backflush_based_on=None):
|
def get_pending_raw_materials(self, backflush_based_on=None):
|
||||||
"""
|
"""
|
||||||
@ -2528,3 +2471,81 @@ def get_supplied_items(purchase_order):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return supplied_item_details
|
return supplied_item_details
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_materials(work_order) -> dict:
|
||||||
|
data = get_stock_entry_data(work_order)
|
||||||
|
|
||||||
|
available_materials = {}
|
||||||
|
for row in data:
|
||||||
|
key = (row.item_code, row.warehouse)
|
||||||
|
if row.purpose != "Material Transfer for Manufacture":
|
||||||
|
key = (row.item_code, row.s_warehouse)
|
||||||
|
|
||||||
|
if key not in available_materials:
|
||||||
|
available_materials.setdefault(
|
||||||
|
key,
|
||||||
|
frappe._dict(
|
||||||
|
{"item_details": row, "batch_details": defaultdict(float), "qty": 0, "serial_nos": []}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
item_data = available_materials[key]
|
||||||
|
|
||||||
|
if row.purpose == "Material Transfer for Manufacture":
|
||||||
|
item_data.qty += row.qty
|
||||||
|
if row.batch_no:
|
||||||
|
item_data.batch_details[row.batch_no] += row.qty
|
||||||
|
|
||||||
|
if row.serial_no:
|
||||||
|
item_data.serial_nos.extend(get_serial_nos(row.serial_no))
|
||||||
|
item_data.serial_nos.sort()
|
||||||
|
else:
|
||||||
|
# Consume raw material qty in case of 'Manufacture' or 'Material Consumption for Manufacture'
|
||||||
|
|
||||||
|
item_data.qty -= row.qty
|
||||||
|
if row.batch_no:
|
||||||
|
item_data.batch_details[row.batch_no] -= row.qty
|
||||||
|
|
||||||
|
if row.serial_no:
|
||||||
|
for serial_no in get_serial_nos(row.serial_no):
|
||||||
|
item_data.serial_nos.remove(serial_no)
|
||||||
|
|
||||||
|
return available_materials
|
||||||
|
|
||||||
|
|
||||||
|
def get_stock_entry_data(work_order):
|
||||||
|
stock_entry = frappe.qb.DocType("Stock Entry")
|
||||||
|
stock_entry_detail = frappe.qb.DocType("Stock Entry Detail")
|
||||||
|
|
||||||
|
return (
|
||||||
|
frappe.qb.from_(stock_entry)
|
||||||
|
.from_(stock_entry_detail)
|
||||||
|
.select(
|
||||||
|
stock_entry_detail.item_name,
|
||||||
|
stock_entry_detail.original_item,
|
||||||
|
stock_entry_detail.item_code,
|
||||||
|
stock_entry_detail.qty,
|
||||||
|
(stock_entry_detail.t_warehouse).as_("warehouse"),
|
||||||
|
(stock_entry_detail.s_warehouse).as_("s_warehouse"),
|
||||||
|
stock_entry_detail.description,
|
||||||
|
stock_entry_detail.stock_uom,
|
||||||
|
stock_entry_detail.expense_account,
|
||||||
|
stock_entry_detail.cost_center,
|
||||||
|
stock_entry_detail.batch_no,
|
||||||
|
stock_entry_detail.serial_no,
|
||||||
|
stock_entry.purpose,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(stock_entry.name == stock_entry_detail.parent)
|
||||||
|
& (stock_entry.work_order == work_order)
|
||||||
|
& (stock_entry.docstatus == 1)
|
||||||
|
& (stock_entry_detail.s_warehouse.isnotnull())
|
||||||
|
& (
|
||||||
|
stock_entry.purpose.isin(
|
||||||
|
["Manufacture", "Material Consumption for Manufacture", "Material Transfer for Manufacture"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orderby(stock_entry.creation, stock_entry_detail.item_code, stock_entry_detail.idx)
|
||||||
|
).run(as_dict=1)
|
||||||
|
@ -44,7 +44,7 @@ Accessable Value,Доступная стоимость,
|
|||||||
Account,Аккаунт,
|
Account,Аккаунт,
|
||||||
Account Number,Номер аккаунта,
|
Account Number,Номер аккаунта,
|
||||||
Account Number {0} already used in account {1},"Номер счета {0}, уже использованный в учетной записи {1}",
|
Account Number {0} already used in account {1},"Номер счета {0}, уже использованный в учетной записи {1}",
|
||||||
Account Pay Only,Счет Оплатить только,
|
Account Pay Only,Только оплатить счет,
|
||||||
Account Type,Тип учетной записи,
|
Account Type,Тип учетной записи,
|
||||||
Account Type for {0} must be {1},Тип счета для {0} должен быть {1},
|
Account Type for {0} must be {1},Тип счета для {0} должен быть {1},
|
||||||
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","Баланс счета в Кредите, запрещена установка 'Баланс должен быть' как 'Дебет'",
|
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","Баланс счета в Кредите, запрещена установка 'Баланс должен быть' как 'Дебет'",
|
||||||
@ -117,7 +117,7 @@ Add Item,Добавить продукт,
|
|||||||
Add Items,Добавить продукты,
|
Add Items,Добавить продукты,
|
||||||
Add Leads,Добавить лид,
|
Add Leads,Добавить лид,
|
||||||
Add Multiple Tasks,Добавить несколько задач,
|
Add Multiple Tasks,Добавить несколько задач,
|
||||||
Add Row,Добавить ряд,
|
Add Row,Добавить строку,
|
||||||
Add Sales Partners,Добавить партнеров по продажам,
|
Add Sales Partners,Добавить партнеров по продажам,
|
||||||
Add Serial No,Добавить серийный номер,
|
Add Serial No,Добавить серийный номер,
|
||||||
Add Students,Добавить студентов,
|
Add Students,Добавить студентов,
|
||||||
@ -692,7 +692,7 @@ Created {0} scorecards for {1} between: ,Созданы {0} оценочные
|
|||||||
Creating Company and Importing Chart of Accounts,Создание компании и импорт плана счетов,
|
Creating Company and Importing Chart of Accounts,Создание компании и импорт плана счетов,
|
||||||
Creating Fees,Создание сборов,
|
Creating Fees,Создание сборов,
|
||||||
Creating Payment Entries......,Создание платежных записей......,
|
Creating Payment Entries......,Создание платежных записей......,
|
||||||
Creating Salary Slips...,Создание зарплатных листков...,
|
Creating Salary Slips...,Создание зарплатных ведомостей...,
|
||||||
Creating student groups,Создание групп студентов,
|
Creating student groups,Создание групп студентов,
|
||||||
Creating {0} Invoice,Создание {0} счета,
|
Creating {0} Invoice,Создание {0} счета,
|
||||||
Credit,Кредит,
|
Credit,Кредит,
|
||||||
@ -995,7 +995,7 @@ Expenses,Расходы,
|
|||||||
Expenses Included In Asset Valuation,"Расходы, включенные в оценку активов",
|
Expenses Included In Asset Valuation,"Расходы, включенные в оценку активов",
|
||||||
Expenses Included In Valuation,"Затрат, включаемых в оценке",
|
Expenses Included In Valuation,"Затрат, включаемых в оценке",
|
||||||
Expired Batches,Просроченные партии,
|
Expired Batches,Просроченные партии,
|
||||||
Expires On,Годен до,
|
Expires On,Актуален до,
|
||||||
Expiring On,Срок действия,
|
Expiring On,Срок действия,
|
||||||
Expiry (In Days),Срок действия (в днях),
|
Expiry (In Days),Срок действия (в днях),
|
||||||
Explore,Обзор,
|
Explore,Обзор,
|
||||||
@ -1411,7 +1411,7 @@ Lab Test UOM,Лабораторная проверка UOM,
|
|||||||
Lab Tests and Vital Signs,Лабораторные тесты и жизненные знаки,
|
Lab Tests and Vital Signs,Лабораторные тесты и жизненные знаки,
|
||||||
Lab result datetime cannot be before testing datetime,Лабораторный результат datetime не может быть до тестирования даты и времени,
|
Lab result datetime cannot be before testing datetime,Лабораторный результат datetime не может быть до тестирования даты и времени,
|
||||||
Lab testing datetime cannot be before collection datetime,Лабораторное тестирование datetime не может быть до даты сбора данных,
|
Lab testing datetime cannot be before collection datetime,Лабораторное тестирование datetime не может быть до даты сбора данных,
|
||||||
Label,Ярлык,
|
Label,Метка,
|
||||||
Laboratory,Лаборатория,
|
Laboratory,Лаборатория,
|
||||||
Language Name,Название языка,
|
Language Name,Название языка,
|
||||||
Large,Большой,
|
Large,Большой,
|
||||||
@ -2874,7 +2874,7 @@ Supplier Id,Id поставщика,
|
|||||||
Supplier Invoice Date cannot be greater than Posting Date,"Дата Поставщик Счет не может быть больше, чем Дата публикации",
|
Supplier Invoice Date cannot be greater than Posting Date,"Дата Поставщик Счет не может быть больше, чем Дата публикации",
|
||||||
Supplier Invoice No,Поставщик Счет №,
|
Supplier Invoice No,Поставщик Счет №,
|
||||||
Supplier Invoice No exists in Purchase Invoice {0},Номер счета поставщика отсутствует в счете на покупку {0},
|
Supplier Invoice No exists in Purchase Invoice {0},Номер счета поставщика отсутствует в счете на покупку {0},
|
||||||
Supplier Name,наименование поставщика,
|
Supplier Name,Наименование поставщика,
|
||||||
Supplier Part No,Деталь поставщика №,
|
Supplier Part No,Деталь поставщика №,
|
||||||
Supplier Quotation,Предложение поставщика,
|
Supplier Quotation,Предложение поставщика,
|
||||||
Supplier Scorecard,Оценочная карта поставщика,
|
Supplier Scorecard,Оценочная карта поставщика,
|
||||||
@ -3091,7 +3091,7 @@ Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total,
|
|||||||
Total Payments,Всего платежей,
|
Total Payments,Всего платежей,
|
||||||
Total Present,Итого Текущая,
|
Total Present,Итого Текущая,
|
||||||
Total Qty,Общее количество,
|
Total Qty,Общее количество,
|
||||||
Total Quantity,Общая численность,
|
Total Quantity,Общее количество,
|
||||||
Total Revenue,Общий доход,
|
Total Revenue,Общий доход,
|
||||||
Total Student,Всего учеников,
|
Total Student,Всего учеников,
|
||||||
Total Target,Всего целей,
|
Total Target,Всего целей,
|
||||||
@ -3498,7 +3498,7 @@ Postal,Почтовый,
|
|||||||
Postal Code,Почтовый индекс,
|
Postal Code,Почтовый индекс,
|
||||||
Previous,Предыдущая,
|
Previous,Предыдущая,
|
||||||
Provider,Поставщик,
|
Provider,Поставщик,
|
||||||
Read Only,Только чтения,
|
Read Only,Только чтение,
|
||||||
Recipient,Сторона-реципиент,
|
Recipient,Сторона-реципиент,
|
||||||
Reviews,Отзывы,
|
Reviews,Отзывы,
|
||||||
Sender,Отправитель,
|
Sender,Отправитель,
|
||||||
@ -3879,7 +3879,7 @@ On Lead Creation,Создание лида,
|
|||||||
On Supplier Creation,Создание поставщика,
|
On Supplier Creation,Создание поставщика,
|
||||||
On Customer Creation,Создание клиента,
|
On Customer Creation,Создание клиента,
|
||||||
Only .csv and .xlsx files are supported currently,В настоящее время поддерживаются только файлы .csv и .xlsx,
|
Only .csv and .xlsx files are supported currently,В настоящее время поддерживаются только файлы .csv и .xlsx,
|
||||||
Only expired allocation can be cancelled,Только истекшее распределение может быть отменено,
|
Only expired allocation can be cancelled,Отменить можно только просроченное распределение,
|
||||||
Only users with the {0} role can create backdated leave applications,Только пользователи с ролью {0} могут создавать оставленные приложения с задним сроком действия,
|
Only users with the {0} role can create backdated leave applications,Только пользователи с ролью {0} могут создавать оставленные приложения с задним сроком действия,
|
||||||
Open,Открыт,
|
Open,Открыт,
|
||||||
Open Contact,Открытый контакт,
|
Open Contact,Открытый контакт,
|
||||||
@ -4046,7 +4046,7 @@ Server Error,Ошибка сервера,
|
|||||||
Service Level Agreement has been changed to {0}.,Соглашение об уровне обслуживания изменено на {0}.,
|
Service Level Agreement has been changed to {0}.,Соглашение об уровне обслуживания изменено на {0}.,
|
||||||
Service Level Agreement was reset.,Соглашение об уровне обслуживания было сброшено.,
|
Service Level Agreement was reset.,Соглашение об уровне обслуживания было сброшено.,
|
||||||
Service Level Agreement with Entity Type {0} and Entity {1} already exists.,Соглашение об уровне обслуживания с типом объекта {0} и объектом {1} уже существует.,
|
Service Level Agreement with Entity Type {0} and Entity {1} already exists.,Соглашение об уровне обслуживания с типом объекта {0} и объектом {1} уже существует.,
|
||||||
Set,Задать,
|
Set,Комплект,
|
||||||
Set Meta Tags,Установить метатеги,
|
Set Meta Tags,Установить метатеги,
|
||||||
Set {0} in company {1},Установить {0} в компании {1},
|
Set {0} in company {1},Установить {0} в компании {1},
|
||||||
Setup,Настройки,
|
Setup,Настройки,
|
||||||
@ -4059,7 +4059,7 @@ Show Stock Ageing Data,Показать данные о старении зап
|
|||||||
Show Warehouse-wise Stock,Показать складской запас,
|
Show Warehouse-wise Stock,Показать складской запас,
|
||||||
Size,Размер,
|
Size,Размер,
|
||||||
Something went wrong while evaluating the quiz.,Что-то пошло не так при оценке теста.,
|
Something went wrong while evaluating the quiz.,Что-то пошло не так при оценке теста.,
|
||||||
Sr,Sr,
|
Sr,№,
|
||||||
Start,Начать,
|
Start,Начать,
|
||||||
Start Date cannot be before the current date,Дата начала не может быть раньше текущей даты,
|
Start Date cannot be before the current date,Дата начала не может быть раньше текущей даты,
|
||||||
Start Time,Время начала,
|
Start Time,Время начала,
|
||||||
@ -4513,7 +4513,7 @@ Mandatory For Profit and Loss Account,Обязательно для счета
|
|||||||
Accounting Period,Период учета,
|
Accounting Period,Период учета,
|
||||||
Period Name,Название периода,
|
Period Name,Название периода,
|
||||||
Closed Documents,Закрытые документы,
|
Closed Documents,Закрытые документы,
|
||||||
Accounts Settings,Настройки аккаунта,
|
Accounts Settings,Настройка счетов,
|
||||||
Settings for Accounts,Настройки для счетов,
|
Settings for Accounts,Настройки для счетов,
|
||||||
Make Accounting Entry For Every Stock Movement,Создавать бухгалтерские проводки при каждом перемещении запасов,
|
Make Accounting Entry For Every Stock Movement,Создавать бухгалтерские проводки при каждом перемещении запасов,
|
||||||
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,"Пользователи с этой ролью могут замороживать счета, а также создавать / изменять бухгалтерские проводки замороженных счетов",
|
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,"Пользователи с этой ролью могут замороживать счета, а также создавать / изменять бухгалтерские проводки замороженных счетов",
|
||||||
@ -5084,8 +5084,8 @@ Allow Zero Valuation Rate,Разрешить нулевую оценку,
|
|||||||
Item Tax Rate,Ставка налогов на продукт,
|
Item Tax Rate,Ставка налогов на продукт,
|
||||||
Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,Налоговый Подробная таблица выбирается из мастера элемента в виде строки и хранится в этой области.\n Используется по налогам и сборам,
|
Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,Налоговый Подробная таблица выбирается из мастера элемента в виде строки и хранится в этой области.\n Используется по налогам и сборам,
|
||||||
Purchase Order Item,Заказ товара,
|
Purchase Order Item,Заказ товара,
|
||||||
Purchase Receipt Detail,Деталь квитанции о покупке,
|
Purchase Receipt Detail,Сведения о квитанции о покупке,
|
||||||
Item Weight Details,Деталь Вес Подробности,
|
Item Weight Details,Сведения о весе товара,
|
||||||
Weight Per Unit,Вес на единицу,
|
Weight Per Unit,Вес на единицу,
|
||||||
Total Weight,Общий вес,
|
Total Weight,Общий вес,
|
||||||
Weight UOM,Вес Единица измерения,
|
Weight UOM,Вес Единица измерения,
|
||||||
@ -5198,7 +5198,7 @@ Address and Contacts,Адрес и контакты,
|
|||||||
Contact List,Список контактов,
|
Contact List,Список контактов,
|
||||||
Hidden list maintaining the list of contacts linked to Shareholder,"Скрытый список, поддерживающий список контактов, связанных с Акционером",
|
Hidden list maintaining the list of contacts linked to Shareholder,"Скрытый список, поддерживающий список контактов, связанных с Акционером",
|
||||||
Specify conditions to calculate shipping amount,Укажите условия для расчета суммы доставки,
|
Specify conditions to calculate shipping amount,Укажите условия для расчета суммы доставки,
|
||||||
Shipping Rule Label,Название правила доставки,
|
Shipping Rule Label,Метка правила доставки,
|
||||||
example: Next Day Shipping,Пример: доставка на следующий день,
|
example: Next Day Shipping,Пример: доставка на следующий день,
|
||||||
Shipping Rule Type,Тип правила доставки,
|
Shipping Rule Type,Тип правила доставки,
|
||||||
Shipping Account,Счет доставки,
|
Shipping Account,Счет доставки,
|
||||||
@ -5236,7 +5236,7 @@ Billing Interval,Интервал выставления счетов,
|
|||||||
Billing Interval Count,Счет интервала фактурирования,
|
Billing Interval Count,Счет интервала фактурирования,
|
||||||
"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Количество интервалов для поля интервалов, например, если Interval является «Days», а количество интервалов фактурирования - 3, счета-фактуры будут генерироваться каждые 3 дня",
|
"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Количество интервалов для поля интервалов, например, если Interval является «Days», а количество интервалов фактурирования - 3, счета-фактуры будут генерироваться каждые 3 дня",
|
||||||
Payment Plan,Платежный план,
|
Payment Plan,Платежный план,
|
||||||
Subscription Plan Detail,Деталь плана подписки,
|
Subscription Plan Detail,Сведения о плана подписки,
|
||||||
Plan,План,
|
Plan,План,
|
||||||
Subscription Settings,Настройки подписки,
|
Subscription Settings,Настройки подписки,
|
||||||
Grace Period,Льготный период,
|
Grace Period,Льготный период,
|
||||||
@ -5802,7 +5802,7 @@ Make Academic Term Mandatory,Сделать академический срок
|
|||||||
Skip User creation for new Student,Пропустить создание пользователя для нового студента,
|
Skip User creation for new Student,Пропустить создание пользователя для нового студента,
|
||||||
"By default, a new User is created for every new Student. If enabled, no new User will be created when a new Student is created.","По умолчанию для каждого нового Студента создается новый Пользователь. Если этот параметр включен, при создании нового Студента новый Пользователь не создается.",
|
"By default, a new User is created for every new Student. If enabled, no new User will be created when a new Student is created.","По умолчанию для каждого нового Студента создается новый Пользователь. Если этот параметр включен, при создании нового Студента новый Пользователь не создается.",
|
||||||
Instructor Records to be created by,Записи инструкторов должны быть созданы,
|
Instructor Records to be created by,Записи инструкторов должны быть созданы,
|
||||||
Employee Number,Общее число сотрудников,
|
Employee Number,Номер сотрудника,
|
||||||
Fee Category,Категория платы,
|
Fee Category,Категория платы,
|
||||||
Fee Component,Компонент платы,
|
Fee Component,Компонент платы,
|
||||||
Fees Category,Категория плат,
|
Fees Category,Категория плат,
|
||||||
@ -6196,7 +6196,7 @@ Inpatient Occupancy,Стационарное размещение,
|
|||||||
Occupancy Status,Статус занятости,
|
Occupancy Status,Статус занятости,
|
||||||
Vacant,Вакантно,
|
Vacant,Вакантно,
|
||||||
Occupied,Занято,
|
Occupied,Занято,
|
||||||
Item Details,Детальная информация о товаре,
|
Item Details,Детальная информация о продукте,
|
||||||
UOM Conversion in Hours,Преобразование UOM в часы,
|
UOM Conversion in Hours,Преобразование UOM в часы,
|
||||||
Rate / UOM,Скорость / UOM,
|
Rate / UOM,Скорость / UOM,
|
||||||
Change in Item,Изменение продукта,
|
Change in Item,Изменение продукта,
|
||||||
@ -6868,8 +6868,8 @@ Only Tax Impact (Cannot Claim But Part of Taxable Income),Только нало
|
|||||||
Create Separate Payment Entry Against Benefit Claim,Создать отдельную заявку на подачу заявки на получение пособия,
|
Create Separate Payment Entry Against Benefit Claim,Создать отдельную заявку на подачу заявки на получение пособия,
|
||||||
Condition and Formula,Состояние и формула,
|
Condition and Formula,Состояние и формула,
|
||||||
Amount based on formula,Сумма на основе формулы,
|
Amount based on formula,Сумма на основе формулы,
|
||||||
Formula,формула,
|
Formula,Формула,
|
||||||
Salary Detail,Заработная плата: Подробности,
|
Salary Detail,Подробно об заработной плате,
|
||||||
Component,Компонент,
|
Component,Компонент,
|
||||||
Do not include in total,Не включать в общей сложности,
|
Do not include in total,Не включать в общей сложности,
|
||||||
Default Amount,По умолчанию количество,
|
Default Amount,По умолчанию количество,
|
||||||
@ -6891,7 +6891,7 @@ Total Principal Amount,Общая сумма,
|
|||||||
Total Interest Amount,Общая сумма процентов,
|
Total Interest Amount,Общая сумма процентов,
|
||||||
Total Loan Repayment,Общая сумма погашения кредита,
|
Total Loan Repayment,Общая сумма погашения кредита,
|
||||||
net pay info,Чистая информация платить,
|
net pay info,Чистая информация платить,
|
||||||
Gross Pay - Total Deduction - Loan Repayment,Gross Pay - Итого Вычет - Погашение кредита,
|
Gross Pay - Total Deduction - Loan Repayment,Валовая заработная плата - Общий вычет - Погашение кредита,
|
||||||
Total in words,Всего в словах,
|
Total in words,Всего в словах,
|
||||||
Net Pay (in words) will be visible once you save the Salary Slip.,"Чистая плата (прописью) будет видна, как только вы сохраните зарплатную ведомость.",
|
Net Pay (in words) will be visible once you save the Salary Slip.,"Чистая плата (прописью) будет видна, как только вы сохраните зарплатную ведомость.",
|
||||||
Salary Component for timesheet based payroll.,Компонент заработной платы для расчета зарплаты на основе расписания.,
|
Salary Component for timesheet based payroll.,Компонент заработной платы для расчета зарплаты на основе расписания.,
|
||||||
@ -6961,7 +6961,7 @@ Trainer Email,Электронная почта тренера,
|
|||||||
Attendees,Присутствующие,
|
Attendees,Присутствующие,
|
||||||
Employee Emails,Электронные почты сотрудников,
|
Employee Emails,Электронные почты сотрудников,
|
||||||
Training Event Employee,Обучение сотрудников Событие,
|
Training Event Employee,Обучение сотрудников Событие,
|
||||||
Invited,приглашенный,
|
Invited,Приглашенный,
|
||||||
Feedback Submitted,Отзыв отправлен,
|
Feedback Submitted,Отзыв отправлен,
|
||||||
Optional,Необязательный,
|
Optional,Необязательный,
|
||||||
Training Result Employee,Результат обучения сотрудника,
|
Training Result Employee,Результат обучения сотрудника,
|
||||||
@ -7185,7 +7185,7 @@ Ordered Quantity,Заказанное количество,
|
|||||||
Item to be manufactured or repacked,Продукт должен быть произведен или переупакован,
|
Item to be manufactured or repacked,Продукт должен быть произведен или переупакован,
|
||||||
Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Количество пункта получены после изготовления / переупаковка от заданных величин сырья,
|
Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Количество пункта получены после изготовления / переупаковка от заданных величин сырья,
|
||||||
Set rate of sub-assembly item based on BOM,Установить скорость сборки на основе спецификации,
|
Set rate of sub-assembly item based on BOM,Установить скорость сборки на основе спецификации,
|
||||||
Allow Alternative Item,Разрешить альтернативный элемент,
|
Allow Alternative Item,Разрешить альтернативный продукт,
|
||||||
Item UOM,Единиц продукта,
|
Item UOM,Единиц продукта,
|
||||||
Conversion Rate,Коэффициент конверсии,
|
Conversion Rate,Коэффициент конверсии,
|
||||||
Rate Of Materials Based On,Оценить материалов на основе,
|
Rate Of Materials Based On,Оценить материалов на основе,
|
||||||
@ -7600,7 +7600,7 @@ Invoices with no Place Of Supply,Счета без места поставки,
|
|||||||
Import Supplier Invoice,Импортная накладная поставщика,
|
Import Supplier Invoice,Импортная накладная поставщика,
|
||||||
Invoice Series,Серия счетов,
|
Invoice Series,Серия счетов,
|
||||||
Upload XML Invoices,Загрузить XML-счета,
|
Upload XML Invoices,Загрузить XML-счета,
|
||||||
Zip File,Zip-файл,
|
Zip File,Zip файл,
|
||||||
Import Invoices,Импорт счетов,
|
Import Invoices,Импорт счетов,
|
||||||
Click on Import Invoices button once the zip file has been attached to the document. Any errors related to processing will be shown in the Error Log.,"Нажмите кнопку «Импортировать счета-фактуры», когда файл zip прикреплен к документу. Любые ошибки, связанные с обработкой, будут отображаться в журнале ошибок.",
|
Click on Import Invoices button once the zip file has been attached to the document. Any errors related to processing will be shown in the Error Log.,"Нажмите кнопку «Импортировать счета-фактуры», когда файл zip прикреплен к документу. Любые ошибки, связанные с обработкой, будут отображаться в журнале ошибок.",
|
||||||
Lower Deduction Certificate,Свидетельство о нижнем удержании,
|
Lower Deduction Certificate,Свидетельство о нижнем удержании,
|
||||||
@ -7635,7 +7635,7 @@ Restaurant Order Entry Item,Номер заказа заказа рестора
|
|||||||
Served,Подается,
|
Served,Подается,
|
||||||
Restaurant Reservation,Бронирование ресторанов,
|
Restaurant Reservation,Бронирование ресторанов,
|
||||||
Waitlisted,Лист ожидания,
|
Waitlisted,Лист ожидания,
|
||||||
No Show,Нет шоу,
|
No Show,Не показывать,
|
||||||
No of People,Нет людей,
|
No of People,Нет людей,
|
||||||
Reservation Time,Время резервирования,
|
Reservation Time,Время резервирования,
|
||||||
Reservation End Time,Время окончания бронирования,
|
Reservation End Time,Время окончания бронирования,
|
||||||
@ -7873,8 +7873,8 @@ Disable In Words,Отключить в словах,
|
|||||||
"If disable, 'In Words' field will not be visible in any transaction","Если отключить, "В словах" поле не будет видно в любой сделке",
|
"If disable, 'In Words' field will not be visible in any transaction","Если отключить, "В словах" поле не будет видно в любой сделке",
|
||||||
Item Classification,Продуктовая классификация,
|
Item Classification,Продуктовая классификация,
|
||||||
General Settings,Основные настройки,
|
General Settings,Основные настройки,
|
||||||
Item Group Name,Пункт Название группы,
|
Item Group Name,Название группы продуктов,
|
||||||
Parent Item Group,Родитель Пункт Группа,
|
Parent Item Group,Родительская группа продукта,
|
||||||
Item Group Defaults,Элемент группы по умолчанию,
|
Item Group Defaults,Элемент группы по умолчанию,
|
||||||
Item Tax,Налог на продукт,
|
Item Tax,Налог на продукт,
|
||||||
Check this if you want to show in website,"Проверьте это, если вы хотите показать в веб-сайт",
|
Check this if you want to show in website,"Проверьте это, если вы хотите показать в веб-сайт",
|
||||||
@ -7971,13 +7971,13 @@ Customs Tariff Number,Номер таможенного тарифа,
|
|||||||
Tariff Number,Тарифный номер,
|
Tariff Number,Тарифный номер,
|
||||||
Delivery To,Доставка,
|
Delivery To,Доставка,
|
||||||
MAT-DN-.YYYY.-,MAT-DN-.YYYY.-,
|
MAT-DN-.YYYY.-,MAT-DN-.YYYY.-,
|
||||||
Is Return,Является Вернуться,
|
Is Return,Возврат,
|
||||||
Issue Credit Note,Кредитная кредитная карта,
|
Issue Credit Note,Кредитная кредитная карта,
|
||||||
Return Against Delivery Note,Вернуться На накладной,
|
Return Against Delivery Note,Возврат по накладной,
|
||||||
Customer's Purchase Order No,Клиентам Заказ Нет,
|
Customer's Purchase Order No,Заказ клиента №,
|
||||||
Billing Address Name,Название адреса для выставления счета,
|
Billing Address Name,Название адреса для выставления счета,
|
||||||
Required only for sample item.,Требуется только для образца пункта.,
|
Required only for sample item.,Требуется только для образца пункта.,
|
||||||
"If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Если вы создали стандартный шаблон в шаблонах Налоги с налогами и сбором платежей, выберите его и нажмите кнопку ниже.",
|
"If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Если вы создали стандартный шаблон в Шаблоне налогов и сборов с продаж, выберите его и нажмите кнопку ниже.",
|
||||||
In Words will be visible once you save the Delivery Note.,По словам будет виден только вы сохраните накладной.,
|
In Words will be visible once you save the Delivery Note.,По словам будет виден только вы сохраните накладной.,
|
||||||
In Words (Export) will be visible once you save the Delivery Note.,В Слов (Экспорт) будут видны только вы сохраните накладной.,
|
In Words (Export) will be visible once you save the Delivery Note.,В Слов (Экспорт) будут видны только вы сохраните накладной.,
|
||||||
Transporter Info,Информация для транспортировки,
|
Transporter Info,Информация для транспортировки,
|
||||||
@ -7991,8 +7991,8 @@ Installation Status,Состояние установки,
|
|||||||
Excise Page Number,Количество Акцизный Страница,
|
Excise Page Number,Количество Акцизный Страница,
|
||||||
Instructions,Инструкции,
|
Instructions,Инструкции,
|
||||||
From Warehouse,Со склада,
|
From Warehouse,Со склада,
|
||||||
Against Sales Order,По Сделке,
|
Against Sales Order,По сделке,
|
||||||
Against Sales Order Item,По Продукту Сделки,
|
Against Sales Order Item,По позиции сделки,
|
||||||
Against Sales Invoice,Повторная накладная,
|
Against Sales Invoice,Повторная накладная,
|
||||||
Against Sales Invoice Item,Счет на продажу продукта,
|
Against Sales Invoice Item,Счет на продажу продукта,
|
||||||
Available Batch Qty at From Warehouse,Доступные Пакетная Кол-во на со склада,
|
Available Batch Qty at From Warehouse,Доступные Пакетная Кол-во на со склада,
|
||||||
@ -8008,7 +8008,7 @@ Delivery Stop,Остановить доставку,
|
|||||||
Lock,Заблокировано,
|
Lock,Заблокировано,
|
||||||
Visited,Посещен,
|
Visited,Посещен,
|
||||||
Order Information,запросить информацию,
|
Order Information,запросить информацию,
|
||||||
Contact Information,Контакты,
|
Contact Information,Контактная информация,
|
||||||
Email sent to,Письмо отправлено,
|
Email sent to,Письмо отправлено,
|
||||||
Dispatch Information,Информация о доставке,
|
Dispatch Information,Информация о доставке,
|
||||||
Estimated Arrival,Ожидаемое прибытие,
|
Estimated Arrival,Ожидаемое прибытие,
|
||||||
@ -8121,7 +8121,7 @@ Two-way,Двусторонний,
|
|||||||
Alternative Item Name,Альтернативное название продукта,
|
Alternative Item Name,Альтернативное название продукта,
|
||||||
Attribute Name,Название атрибута,
|
Attribute Name,Название атрибута,
|
||||||
Numeric Values,Числовые значения,
|
Numeric Values,Числовые значения,
|
||||||
From Range,От хребта,
|
From Range,Из диапазона,
|
||||||
Increment,Приращение,
|
Increment,Приращение,
|
||||||
To Range,В диапазоне,
|
To Range,В диапазоне,
|
||||||
Item Attribute Values,Пункт значений атрибутов,
|
Item Attribute Values,Пункт значений атрибутов,
|
||||||
@ -8143,7 +8143,7 @@ Default Supplier,Поставщик по умолчанию,
|
|||||||
Default Expense Account,Счет учета затрат по умолчанию,
|
Default Expense Account,Счет учета затрат по умолчанию,
|
||||||
Sales Defaults,По умолчанию,
|
Sales Defaults,По умолчанию,
|
||||||
Default Selling Cost Center,По умолчанию Продажа Стоимость центр,
|
Default Selling Cost Center,По умолчанию Продажа Стоимость центр,
|
||||||
Item Manufacturer,Пункт Производитель,
|
Item Manufacturer,Производитель товара,
|
||||||
Item Price,Цена продукта,
|
Item Price,Цена продукта,
|
||||||
Packing Unit,Упаковочный блок,
|
Packing Unit,Упаковочный блок,
|
||||||
Quantity that must be bought or sold per UOM,"Количество, которое необходимо купить или продать за UOM",
|
Quantity that must be bought or sold per UOM,"Количество, которое необходимо купить или продать за UOM",
|
||||||
@ -8177,7 +8177,7 @@ Purchase Receipts,Покупка Поступления,
|
|||||||
Purchase Receipt Items,Покупка продуктов,
|
Purchase Receipt Items,Покупка продуктов,
|
||||||
Get Items From Purchase Receipts,Получить продукты из покупки.,
|
Get Items From Purchase Receipts,Получить продукты из покупки.,
|
||||||
Distribute Charges Based On,Распределите платежи на основе,
|
Distribute Charges Based On,Распределите платежи на основе,
|
||||||
Landed Cost Help,Земельные Стоимость Помощь,
|
Landed Cost Help,Справка по стоимости доставки,
|
||||||
Manufacturers used in Items,Производители использовали в пунктах,
|
Manufacturers used in Items,Производители использовали в пунктах,
|
||||||
Limited to 12 characters,Ограничено до 12 символов,
|
Limited to 12 characters,Ограничено до 12 символов,
|
||||||
MAT-MR-.YYYY.-,МАТ-MR-.YYYY.-,
|
MAT-MR-.YYYY.-,МАТ-MR-.YYYY.-,
|
||||||
@ -8186,13 +8186,13 @@ Transferred,Переданы,
|
|||||||
% Ordered,% заказано,
|
% Ordered,% заказано,
|
||||||
Terms and Conditions Content,Условия Содержимое,
|
Terms and Conditions Content,Условия Содержимое,
|
||||||
Quantity and Warehouse,Количество и Склад,
|
Quantity and Warehouse,Количество и Склад,
|
||||||
Lead Time Date,Время и Дата Лида,
|
Lead Time Date,Дата выполнения заказа,
|
||||||
Min Order Qty,Минимальный заказ Кол-во,
|
Min Order Qty,Минимальное количество для заказа,
|
||||||
Packed Item,Упаковано,
|
Packed Item,Упаковано,
|
||||||
To Warehouse (Optional),На склад (Необязательно),
|
To Warehouse (Optional),На склад (Необязательно),
|
||||||
Actual Batch Quantity,Фактическое количество партий,
|
Actual Batch Quantity,Фактическое количество партий,
|
||||||
Prevdoc DocType,Prevdoc DocType,
|
Prevdoc DocType,Prevdoc DocType,
|
||||||
Parent Detail docname,Родитель Деталь DOCNAME,
|
Parent Detail docname,Сведения о родителе docname,
|
||||||
"Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.","Создаёт упаковочные листы к упаковкам для доставки. Содержит номер упаковки, перечень содержимого и вес.",
|
"Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.","Создаёт упаковочные листы к упаковкам для доставки. Содержит номер упаковки, перечень содержимого и вес.",
|
||||||
Indicates that the package is a part of this delivery (Only Draft),"Указывает, что пакет является частью этой поставки (только проект)",
|
Indicates that the package is a part of this delivery (Only Draft),"Указывает, что пакет является частью этой поставки (только проект)",
|
||||||
MAT-PAC-.YYYY.-,MAT-PAC-.YYYY.-,
|
MAT-PAC-.YYYY.-,MAT-PAC-.YYYY.-,
|
||||||
@ -8353,7 +8353,7 @@ Automatically Set Serial Nos based on FIFO,Автоматически устан
|
|||||||
Auto Material Request,Автоматический запрос материалов,
|
Auto Material Request,Автоматический запрос материалов,
|
||||||
Inter Warehouse Transfer Settings,Настройки передачи между складами,
|
Inter Warehouse Transfer Settings,Настройки передачи между складами,
|
||||||
Freeze Stock Entries,Замораживание поступления запасов,
|
Freeze Stock Entries,Замораживание поступления запасов,
|
||||||
Stock Frozen Upto,остатки заморожены до,
|
Stock Frozen Upto,Остатки заморожены до,
|
||||||
Batch Identification,Идентификация партии,
|
Batch Identification,Идентификация партии,
|
||||||
Use Naming Series,Использовать серийный номер,
|
Use Naming Series,Использовать серийный номер,
|
||||||
Naming Series Prefix,Префикс Идентификации по Имени,
|
Naming Series Prefix,Префикс Идентификации по Имени,
|
||||||
@ -8372,7 +8372,7 @@ Issue Split From,Выпуск Сплит От,
|
|||||||
Service Level,Уровень обслуживания,
|
Service Level,Уровень обслуживания,
|
||||||
Response By,Ответ от,
|
Response By,Ответ от,
|
||||||
Response By Variance,Ответ по отклонениям,
|
Response By Variance,Ответ по отклонениям,
|
||||||
Ongoing,постоянный,
|
Ongoing,Постоянный,
|
||||||
Resolution By,Разрешение по,
|
Resolution By,Разрешение по,
|
||||||
Resolution By Variance,Разрешение по отклонениям,
|
Resolution By Variance,Разрешение по отклонениям,
|
||||||
Service Level Agreement Creation,Создание соглашения об уровне обслуживания,
|
Service Level Agreement Creation,Создание соглашения об уровне обслуживания,
|
||||||
|
Can't render this file because it is too large.
|
Loading…
Reference in New Issue
Block a user