Merge branch 'develop' of https://github.com/frappe/erpnext into develop

This commit is contained in:
Sathisha Poojary 2018-03-31 00:55:56 +05:30
commit f069e1c83c
43 changed files with 1381 additions and 113 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '10.1.15'
__version__ = '10.1.18'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -4,12 +4,21 @@
.print-format {
padding: 8mm;
margin:4mm;
font-size:10px;
font-size: 10.0pt !important;
font-family: Tahoma, sans-serif;
}
}
</style>
{% } %}
<style>
.print-format {
padding: 8mm;
margin:4mm;
font-size: 10.0pt !important;
font-family: Tahoma, sans-serif;
}
</style>
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h4 class="text-center">{%= filters.customer || filters.supplier %} </h4>
@ -91,8 +100,8 @@
<thead>
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 15%">{%= __("Ref") %}</th>
<th style="width: 15%">{%= __("Date") %}</th>
<th style="width: 20%">{%= __("Reference") %}</th>
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
@ -125,10 +134,15 @@
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
<td>{%= dateutil.str_to_user(data[i][__("Posting Date")]) %}</td>
<td>{%= data[i][__("Voucher Type")] %}
<br>{%= data[i][__("Voucher No")] %}</td>
{% if(!filters.show_pdc_in_print) { %}
<td>{%= dateutil.str_to_user(data[i]["posting_date"]) %}</td>
<td>
{% if(!filters.show_pdc_in_print) { %}
{%= data[i]["voucher_type"] %}
<br>
{% } %}
{%= data[i]["voucher_no"] %}
</td>
{% if(!filters.show_pdc_in_print) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}

View File

@ -22,13 +22,35 @@ class ReceivablePayableReport(object):
return columns, data, None, chart
def get_columns(self, party_naming_by, args):
columns = [_("Posting Date") + ":Date:80", _(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
columns = []
columns.append({
"label": _("Posting Date"),
"fieldtype": "Date",
"fieldname": "posting_date",
"width": 90
})
columns += [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
if party_naming_by == "Naming Series":
columns += [args.get("party_type") + " Name::110"]
columns += [_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/"+_("Voucher Type")+":120",
_("Due Date") + ":Date:80"]
columns.append({
"label": _("Voucher Type"),
"fieldtype": "Data",
"fieldname": "voucher_type",
"width": 110
})
columns.append({
"label": _("Voucher No"),
"fieldtype": "Dynamic Link",
"fieldname": "voucher_no",
"width": 110,
"options": "voucher_type",
})
columns += [_("Due Date") + ":Date:80"]
if args.get("party_type") == "Supplier":
columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"]
@ -114,7 +136,7 @@ class ReceivablePayableReport(object):
return_entries = self.get_return_entries(args.get("party_type"))
data = []
pdc_details = get_pdc_details(args.get("party_type"))
pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
@ -160,6 +182,7 @@ class ReceivablePayableReport(object):
row.append(company_currency)
pdc = pdc_details.get((gle.voucher_no, gle.party), {})
remaining_balance = outstanding_amount - flt(pdc.get("pdc_amount"))
row += [pdc.get("pdc_date"), pdc.get("pdc_ref"),
flt(pdc.get("pdc_amount")), remaining_balance]
@ -392,7 +415,7 @@ def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_dat
return [age] + outstanding_range
def get_pdc_details(party_type):
def get_pdc_details(party_type, report_date):
pdc_details = frappe._dict()
for pdc in frappe.db.sql("""
@ -405,13 +428,14 @@ def get_pdc_details(party_type):
on
(pref.parent = pent.name)
where
pent.docstatus < 2 and pent.reference_date >= pent.posting_date
pent.docstatus < 2 and pent.reference_date >= %s
and pent.party_type = %s
group by pent.party, pref.reference_name""", party_type, as_dict=1):
group by pent.party, pref.reference_name""", (report_date, party_type), as_dict=1):
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
if scrub(party_type):
amount_field = "jea.debit_in_account_currency + jea.credit_in_account_currency"
amount_field = ("jea.debit_in_account_currency"
if party_type == 'Supplier' else "jea.credit_in_account_currency")
else:
amount_field = "jea.debit + jea.credit"
@ -425,9 +449,9 @@ def get_pdc_details(party_type):
on
(jea.parent = je.name)
where
je.docstatus < 2 and je.cheque_date >= je.posting_date
je.docstatus < 2 and je.cheque_date >= %s
and jea.party_type = %s
group by jea.party, jea.reference_name""".format(amount_field), party_type, as_dict=1):
group by jea.party, jea.reference_name""".format(amount_field), (report_date, party_type), as_dict=1):
if (pdc.invoice_no, pdc.party) in pdc_details:
pdc_details[(pdc.invoice_no, pdc.party)]["pdc_amount"] += pdc.pdc_amount
else:

View File

@ -423,10 +423,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
if fg_items:
items = tuple(set(d["rm_item_code"] for d in rm_items_list))
item_wh = frappe._dict(frappe.db.sql("""
select item_code, description
from `tabItem` where name in ({0})
""".format(", ".join(["%s"] * len(items))), items))
item_wh = get_item_details(items)
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = "Subcontract"
@ -441,13 +438,15 @@ def make_rm_stock_entry(purchase_order, rm_items):
for item_code in fg_items:
for rm_item_data in rm_items_list:
if rm_item_data["item_code"] == item_code:
rm_item_code = rm_item_data["rm_item_code"]
items_dict = {
rm_item_data["rm_item_code"]: {
rm_item_code: {
"item_name": rm_item_data["item_name"],
"description": item_wh.get(rm_item_data["rm_item_code"]),
"description": item_wh[rm_item_code].get('description'),
'qty': rm_item_data["qty"],
'from_warehouse': rm_item_data["warehouse"],
'stock_uom': rm_item_data["stock_uom"]
'stock_uom': rm_item_data["stock_uom"],
'allow_alternative_item': item_wh[rm_item_code].get('allow_alternative_item')
}
}
stock_entry.add_to_stock_entry_detail(items_dict)
@ -456,6 +455,14 @@ def make_rm_stock_entry(purchase_order, rm_items):
frappe.throw(_("No Items selected for transfer"))
return purchase_order.name
def get_item_details(items):
item_details = {}
for d in frappe.db.sql("""select item_code, description, allow_alternative_item from `tabItem`
where name in ({0})""".format(", ".join(["%s"] * len(items))), items, as_dict=1):
item_details[d.item_code] = d
return item_details
@frappe.whitelist()
def update_status(status, name):
po = frappe.get_doc("Purchase Order", name)

View File

@ -321,6 +321,7 @@ def create_purchase_order(**args):
po.is_subcontracted = args.is_subcontracted or "No"
po.currency = args.currency or frappe.db.get_value("Company", po.company, "default_currency")
po.conversion_factor = args.conversion_factor or 1
po.supplier_warehouse = args.supplier_warehouse or None
po.append("items", {
"item_code": args.item or args.item_code or "_Test Item",

View File

@ -71,6 +71,10 @@ def get_data():
"type": "doctype",
"name": "Item",
},
{
"type": "doctype",
"name": "Item Alternative",
},
{
"type": "doctype",
"name": "Product Bundle",

View File

@ -10,6 +10,7 @@ from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items
from erpnext.controllers.stock_controller import StockController
@ -200,6 +201,11 @@ class BuyingController(StockController):
exploded_item = item.get('include_exploded_items')
bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item)
used_alternative_items = []
if self.doctype == 'Purchase Receipt' and item.purchase_order:
used_alternative_items = get_used_alternative_items(purchase_order = item.purchase_order)
raw_materials_cost = 0
items = list(set([d.item_code for d in bom_items]))
item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse
@ -211,6 +217,16 @@ class BuyingController(StockController):
if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company:
reserve_warehouse = None
conversion_factor = item.conversion_factor
if (self.doctype == 'Purchase Receipt' and item.purchase_order and
bom_item.item_code in used_alternative_items):
alternative_item_data = used_alternative_items.get(bom_item.item_code)
bom_item.item_code = alternative_item_data.item_code
bom_item.item_name = alternative_item_data.item_name
bom_item.stock_uom = alternative_item_data.stock_uom
conversion_factor = alternative_item_data.conversion_factor
bom_item.description = alternative_item_data.description
# check if exists
exists = 0
for d in self.get(raw_material_table):
@ -223,7 +239,7 @@ class BuyingController(StockController):
rm = self.append(raw_material_table, {})
required_qty = flt(flt(bom_item.qty_consumed_per_unit) * (flt(item.qty) + getattr(item, 'rejected_qty', 0)) *
flt(item.conversion_factor), rm.precision("required_qty"))
flt(conversion_factor), rm.precision("required_qty"))
rm.reference_name = item.name
rm.bom_detail_no = bom_item.name
rm.main_item_code = item.item_code
@ -233,7 +249,7 @@ class BuyingController(StockController):
if self.doctype == "Purchase Order" and not rm.reserve_warehouse:
rm.reserve_warehouse = reserve_warehouse
rm.conversion_factor = item.conversion_factor
rm.conversion_factor = conversion_factor
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
rm.consumed_qty = required_qty
@ -463,4 +479,4 @@ def get_items_from_bom(item_code, bom, exploded_item=1):
if not bom_items:
msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1)
return bom_items
return bom_items

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -4,6 +4,7 @@ work-order
workstation
operation
subcontracting
item-alternative
tools
setup
articles

View File

@ -0,0 +1,32 @@
# Item Alternative
Item alternative feature is very useful in manufacturing industries, if the raw material defined in the BOM is not available during the production process then their respective available alternative item used to complete the production process.
To make item alaternative for an item, kindly enable the "Allow Alternative Item" in the item.
<img class="screenshot" alt="Item" src="{{docs_base_url}}/assets/img/manufacturing/allow-alternative-item.png">
* To make item alternative, goto module Stock > Items and Pricing > Item Alternative
<img class="screenshot" alt="Item Alternative" src="{{docs_base_url}}/assets/img/manufacturing/item-alternative.png">
The user can enable Two-Way between an item and their alternative item if both can be used as an alternative to each other
### Item Alternative for work order
To allow to use alternative items in the manufacturing process user can configure allow an alternative item in the BOM/Work Order
##### Provision to allow alternative item in the bom
<img class="screenshot" alt="Item" src="{{docs_base_url}}/assets/img/manufacturing/allow-alternative-item-bom.png">
##### Provision to allow alternative item in the work order
User can also enable/disable allow alternative item in the work order
<img class="screenshot" alt="Item" src="{{docs_base_url}}/assets/img/manufacturing/allow-alternative-item-wo.png">
##### How it works for work order
<img class="screenshot" alt="Item" src="{{docs_base_url}}/assets/img/manufacturing/work_order_item_alternative.gif">
### Item Alternative for subcontract
In subcontract, the user has to transfer raw materials to the subcontracted supplier to get finished good from them. If the raw material is not available in the stock, with this feature, the user can transfer the alternate item of the subcontracted raw material to the supplier.
##### How it works for subcontract
<img class="screenshot" alt="Item" src="{{docs_base_url}}/assets/img/manufacturing/purchase_order_item_alternative.gif">

View File

@ -42,6 +42,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -73,6 +74,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -106,6 +108,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -136,6 +139,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -168,6 +172,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -196,6 +201,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -228,6 +234,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -260,6 +267,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -290,6 +298,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -320,6 +329,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -351,6 +361,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -382,6 +393,38 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Alternative Item",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -412,6 +455,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -443,6 +487,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -472,6 +517,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -503,6 +549,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -533,6 +580,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -565,6 +613,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -597,6 +646,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -627,6 +677,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -659,6 +710,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -689,6 +741,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -720,6 +773,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -750,6 +804,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -780,6 +835,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -810,6 +866,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -841,6 +898,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -869,6 +927,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -900,6 +959,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -931,6 +991,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -962,6 +1023,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -991,6 +1053,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1021,6 +1084,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1050,6 +1114,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1081,6 +1146,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1110,6 +1176,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1142,6 +1209,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1172,6 +1240,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1200,6 +1269,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1230,6 +1300,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1259,6 +1330,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1288,6 +1360,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1317,6 +1390,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1347,6 +1421,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1378,6 +1453,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1408,6 +1484,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1440,6 +1517,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1471,6 +1549,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1502,6 +1581,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1532,6 +1612,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1564,6 +1645,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1594,6 +1676,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1626,6 +1709,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1657,6 +1741,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1688,6 +1773,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1719,6 +1805,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -1733,7 +1820,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-16 13:43:55.485813",
"modified": "2018-02-26 22:51:40.232456",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@ -543,6 +543,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
item.description,
item.image,
item.stock_uom,
item.allow_alternative_item,
item.default_warehouse,
item.expense_account as expense_account,
item.buying_cost_center as cost_center
@ -606,6 +607,9 @@ def validate_bom_no(item, bom_no):
for d in bom.items:
if (d.item_code.lower() == item.lower()):
rm_item_exists = True
for d in bom.scrap_items:
if (d.item_code.lower() == item.lower()):
rm_item_exists = True
if bom.item.lower() == item.lower() or \
bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower():
rm_item_exists = True

View File

@ -171,7 +171,7 @@ frappe.ui.form.on("Work Order", {
frm.set_value('sales_order', "");
frm.trigger('set_sales_order');
erpnext.in_production_item_onchange = true;
$.each(["description", "stock_uom", "project", "bom_no"], function(i, field) {
$.each(["description", "stock_uom", "project", "bom_no", "allow_alternative_item"], function(i, field) {
frm.set_value(field, r.message[field]);
});

View File

@ -40,6 +40,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -71,6 +72,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 1,
"translatable": 0,
"unique": 0
},
{
@ -105,6 +107,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -137,6 +140,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -171,6 +175,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -202,6 +207,38 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Alternative Item",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -231,6 +268,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50%"
},
@ -264,6 +302,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -297,6 +336,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -331,6 +371,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -362,6 +403,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -394,6 +436,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -425,6 +468,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -455,6 +499,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -485,6 +530,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -517,6 +563,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -545,6 +592,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -576,6 +624,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -606,6 +655,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -637,6 +687,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -668,6 +719,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -699,6 +751,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -729,6 +782,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -758,6 +812,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -788,6 +843,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -818,6 +874,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -848,6 +905,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -880,6 +938,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -912,6 +971,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -944,6 +1004,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -975,6 +1036,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1006,6 +1068,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1037,6 +1100,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1066,6 +1130,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1097,6 +1162,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1127,6 +1193,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1156,6 +1223,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1189,6 +1257,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1221,6 +1290,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1249,6 +1319,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50%"
},
@ -1282,6 +1353,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1312,6 +1384,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1342,6 +1415,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1373,6 +1447,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1403,6 +1478,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1435,6 +1511,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -1449,7 +1526,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-13 02:58:11.328693",
"modified": "2018-03-05 12:43:10.442928",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",

View File

@ -489,6 +489,7 @@ class WorkOrder(Document):
'item_code': item.item_code,
'item_name': item.item_name,
'description': item.description,
'allow_alternative_item': item.allow_alternative_item,
'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse
})
@ -503,15 +504,17 @@ class WorkOrder(Document):
transferred_qty = frappe.db.sql('''select sum(qty)
from `tabStock Entry` entry, `tabStock Entry Detail` detail
where
entry.work_order = %s
entry.work_order = %(name)s
and entry.purpose = "Material Transfer for Manufacture"
and entry.docstatus = 1
and detail.parent = entry.name
and detail.item_code = %s''', (self.name, d.item_code))[0][0]
and (detail.item_code = %(item)s or detail.original_item = %(item)s)''', {
'name': self.name,
'item': d.item_code
})[0][0]
d.db_set('transferred_qty', flt(transferred_qty), update_modified = False)
@frappe.whitelist()
def get_item_details(item, project = None):
res = frappe.db.sql("""
@ -548,6 +551,7 @@ def get_item_details(item, project = None):
frappe.throw(_("Default BOM for {0} not found").format(item))
res['project'] = project or frappe.db.get_value('BOM', res['bom_no'], 'project')
res['allow_alternative_item'] = frappe.db.get_value('BOM', res['bom_no'], 'allow_alternative_item')
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
return res

View File

@ -41,6 +41,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -72,6 +73,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -101,6 +103,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -131,6 +134,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -161,6 +165,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -191,6 +196,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -221,6 +227,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -252,6 +259,38 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Alternative Item",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -281,6 +320,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -311,6 +351,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -341,6 +382,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -354,7 +396,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-02-13 02:58:11.328693",
"modified": "2018-03-05 13:07:07.530725",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Item",

View File

@ -6,6 +6,8 @@ import frappe
from erpnext.stock.utils import get_bin
def execute():
frappe.reload_doc("stock", "doctype", "bin")
frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied")
for d in frappe.db.sql("""
select distinct rm_item_code, reserve_warehouse
from `tabPurchase Order Item Supplied`
@ -26,4 +28,4 @@ def execute():
bin_doc = get_bin(d[0], d[1])
bin_doc.update_reserved_qty_for_production()
except:
pass
pass

View File

@ -42,6 +42,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -59,7 +60,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 1,
@ -75,6 +76,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -91,7 +93,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Project Type",
"length": 0,
"no_copy": 0,
@ -107,6 +109,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -139,6 +142,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -171,6 +175,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -200,6 +205,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -216,7 +222,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Priority",
"length": 0,
"no_copy": 0,
@ -232,6 +238,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -263,6 +270,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -294,6 +302,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -323,6 +332,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -354,6 +364,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -386,6 +397,7 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -415,6 +427,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -446,6 +459,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -476,6 +490,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -508,6 +523,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -539,6 +555,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -570,6 +587,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -600,6 +618,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -631,6 +650,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -662,6 +682,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -692,6 +713,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -722,6 +744,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -752,6 +775,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -781,6 +805,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -812,6 +837,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -843,6 +869,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -875,6 +902,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -906,6 +934,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -937,6 +966,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -967,6 +997,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -997,6 +1028,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1026,6 +1058,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1056,6 +1089,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1087,6 +1121,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1117,6 +1152,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1147,6 +1183,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1177,6 +1214,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1207,6 +1245,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50%"
},
@ -1240,6 +1279,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1269,6 +1309,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1301,6 +1342,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1332,6 +1374,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1363,6 +1406,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1395,6 +1439,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1424,6 +1469,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1455,6 +1501,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1486,6 +1533,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1517,6 +1565,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1548,6 +1597,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1579,6 +1629,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1611,6 +1662,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1642,6 +1694,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -1656,7 +1709,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 4,
"modified": "2018-03-22 11:44:38.723507",
"modified": "2018-03-28 10:19:32.743900",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project",

View File

@ -184,6 +184,142 @@ $.extend(erpnext.utils, {
},
});
erpnext.utils.select_alternate_items = function(opts) {
const frm = opts.frm;
const warehouse_field = opts.warehouse_field || 'warehouse';
const item_field = opts.item_field || 'item_code';
this.data = [];
const dialog = new frappe.ui.Dialog({
title: __("Select Alternate Item"),
fields: [
{fieldtype:'Section Break', label: __('Items')},
{
fieldname: "alternative_items", fieldtype: "Table", cannot_add_rows: true,
in_place_edit: true, data: this.data,
get_data: () => {
return this.data;
},
fields: [{
fieldtype:'Data',
fieldname:"docname",
hidden: 1
}, {
fieldtype:'Link',
fieldname:"item_code",
options: 'Item',
in_list_view: 1,
read_only: 1,
label: __('Item Code')
}, {
fieldtype:'Link',
fieldname:"alternate_item",
options: 'Item',
default: "",
in_list_view: 1,
label: __('Alternate Item'),
onchange: function() {
const item_code = this.get_value();
const warehouse = this.grid_row.on_grid_fields_dict.warehouse.get_value();
if (item_code && warehouse) {
frappe.call({
method: "erpnext.stock.utils.get_latest_stock_qty",
args: {
item_code: item_code,
warehouse: warehouse
},
callback: (r) => {
this.grid_row.on_grid_fields_dict
.actual_qty.set_value(r.message || 0);
}
})
}
},
get_query: (e) => {
return {
query: "erpnext.stock.doctype.item_alternative.item_alternative.get_alternative_items",
filters: {
item_code: e.item_code
}
};
}
}, {
fieldtype:'Link',
fieldname:"warehouse",
options: 'Warehouse',
default: "",
in_list_view: 1,
label: __('Warehouse'),
onchange: function() {
const warehouse = this.get_value();
const item_code = this.grid_row.on_grid_fields_dict.item_code.get_value();
if (item_code && warehouse) {
frappe.call({
method: "erpnext.stock.utils.get_latest_stock_qty",
args: {
item_code: item_code,
warehouse: warehouse
},
callback: (r) => {
this.grid_row.on_grid_fields_dict
.actual_qty.set_value(r.message || 0);
}
})
}
},
}, {
fieldtype:'Float',
fieldname:"actual_qty",
default: 0,
read_only: 1,
in_list_view: 1,
label: __('Available Qty')
}]
},
],
primary_action: function() {
const args = this.get_values()["alternative_items"];
const alternative_items = args.filter(d => {
if (d.alternate_item && d.item_code != d.alternate_item) {
return true;
}
});
alternative_items.forEach(d => {
let row = frappe.get_doc(opts.child_doctype, d.docname);
let qty = row.qty;
row[item_field] = d.alternate_item;
frm.script_manager.trigger(item_field, row.doctype, row.name)
.then(() => {
frappe.model.set_value(row.doctype, row.name, 'qty', qty);
frappe.model.set_value(row.doctype, row.name,
opts.original_item_field, d.item_code);
});
});
refresh_field(opts.child_docname);
this.hide();
},
primary_action_label: __('Update')
});
frm.doc[opts.child_docname].forEach(d => {
if (!opts.condition || opts.condition(d)) {
dialog.fields_dict.alternative_items.df.data.push({
"docname": d.name,
"item_code": d[item_field],
"warehouse": d[warehouse_field],
"actual_qty": d.actual_qty
});
}
})
this.data = dialog.fields_dict.alternative_items.df.data;
dialog.fields_dict.alternative_items.grid.refresh();
dialog.show();
}
erpnext.utils.map_current_doc = function(opts) {
if(opts.get_query_filters) {
opts.get_query = function() {

View File

@ -319,7 +319,8 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
["parent", "=", $(e.target).attr("data-fieldname")],
["attribute_value", "like", e.target.value + "%"]
],
fields: ["attribute_value"]
fields: ["attribute_value"],
parent: "Item"
},
callback: function(r) {
if (r.message) {

View File

@ -85,8 +85,6 @@ erpnext.SerialNoBatchSelector = Class.extend({
fields: fields
});
this.bind_qty();
this.dialog.set_primary_action(__('Insert'), function() {
me.values = me.dialog.get_values();
if(me.validate()) {
@ -102,17 +100,24 @@ erpnext.SerialNoBatchSelector = Class.extend({
}
if (d.batch_no) {
this.dialog.fields_dict.batches.df.data.push({
'batch_no': d.batch_no,
'actual_qty': d.actual_qty,
'selected_qty': d.qty,
'available_qty': d.actual_batch_qty
this.frm.doc.items.forEach(data => {
if(data.item_code == d.item_code) {
this.dialog.fields_dict.batches.df.data.push({
'batch_no': data.batch_no,
'actual_qty': data.actual_qty,
'selected_qty': data.qty,
'available_qty': data.actual_batch_qty
});
}
});
this.dialog.fields_dict.batches.grid.refresh();
}
}
if (this.has_batch) {
this.update_total_qty();
}
this.dialog.show();
},
@ -161,18 +166,44 @@ erpnext.SerialNoBatchSelector = Class.extend({
var me = this;
if(this.has_batch) {
this.values.batches.map((batch, i) => {
let item_code_field = {};
let row = (i !== 0) ? this.frm.add_child("items", this.item) : this.item;
let batch_no = batch.batch_no;
let row = '';
if (i !== 0 && !this.batch_exists(batch_no)) {
row = this.frm.add_child("items", {
'item_code': this.item.item_code,
'item_name': this.item.item_name,
'price_list_rate': this.item.price_list_rate,
'rate': this.item.rate,
'qty': batch.selected_qty,
'batch_no': batch_no,
'actual_qty': this.item.actual_qty,
'discount_percentage': this.item.discount_percentage
});
} else {
row = this.frm.doc.items.find(i => i.batch_no === batch_no);
}
if (!row) {
row = this.item;
}
this.map_row_values(row, batch, 'batch_no',
'selected_qty', this.values.warehouse);
});
} else {
this.map_row_values(this.item, this.values, 'serial_no', 'qty');
}
refresh_field("items");
this.callback && this.callback(this.item);
},
batch_exists: function(batch) {
const batches = this.frm.doc.items.map(data => data.batch_no);
return (batches && in_list(batches, batch)) ? true : false;
},
map_row_values: function(row, values, number, qty_field, warehouse) {
row.qty = values[qty_field];
row[number] = values[number];
@ -185,20 +216,15 @@ erpnext.SerialNoBatchSelector = Class.extend({
}
},
bind_qty: function() {
let batches_field = this.dialog.fields_dict.batches;
update_total_qty: function() {
let qty_field = this.dialog.fields_dict.qty;
if(batches_field) {
batches_field.grid.wrapper.on('change', function() {
let total_qty = 0;
batches_field.grid.wrapper.find(
'input[data-fieldname="selected_qty"]').each(function() {
let total_qty = 0;
total_qty += Number($(this).val());
});
qty_field.set_input(total_qty);
});
}
this.dialog.fields_dict.batches.df.data.forEach(data => {
total_qty += flt(data.selected_qty);
});
qty_field.set_input(total_qty);
},
get_batch_fields: function() {
@ -230,7 +256,10 @@ erpnext.SerialNoBatchSelector = Class.extend({
if(row === this.grid_row) {
return "";
}
return row.on_grid_fields_dict.batch_no.get_value();
if (row.on_grid_fields_dict.batch_no) {
return row.on_grid_fields_dict.batch_no.get_value();
}
});
if(selected_batches.includes(val)) {
this.set_value("");
@ -293,6 +322,8 @@ erpnext.SerialNoBatchSelector = Class.extend({
} else {
this.grid.refresh();
}
me.update_total_qty();
}
},
],

View File

@ -9,7 +9,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
});
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => {
if (r && r.use_pos_in_offline_mode && !cint(r.use_pos_in_offline_mode)) {
if (r && !cint(r.use_pos_in_offline_mode)) {
// online
wrapper.pos = new erpnext.pos.PointOfSale(wrapper);
window.cur_pos = wrapper.pos;
@ -23,7 +23,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
frappe.pages['point-of-sale'].refresh = function(wrapper) {
if (wrapper.pos) {
cur_frm = wrapper.pos.frm;
wrapper.pos.make_new_invoice();
}
if (frappe.flags.is_offline) {
@ -96,8 +96,8 @@ erpnext.pos.PointOfSale = class PointOfSale {
wrapper: this.wrapper.find('.cart-container'),
events: {
on_customer_change: (customer) => this.frm.set_value('customer', customer),
on_field_change: (item_code, field, value) => {
this.update_item_in_cart(item_code, field, value);
on_field_change: (item_code, field, value, batch_no) => {
this.update_item_in_cart(item_code, field, value, batch_no);
},
on_numpad: (value) => {
if (value == 'Pay') {
@ -158,10 +158,12 @@ erpnext.pos.PointOfSale = class PointOfSale {
});
}
update_item_in_cart(item_code, field='qty', value=1) {
update_item_in_cart(item_code, field='qty', value=1, batch_no) {
frappe.dom.freeze();
if(this.cart.exists(item_code)) {
const item = this.frm.doc.items.find(i => i.item_code === item_code);
if(this.cart.exists(item_code, batch_no)) {
const search_field = batch_no ? 'batch_no' : 'item_code';
const search_value = batch_no || item_code;
const item = this.frm.doc.items.find(i => i[search_field] === search_value);
frappe.flags.hide_serial_batch_dialog = false;
if (typeof value === 'string' && !in_list(['serial_no', 'batch_no'], field)) {
@ -219,29 +221,31 @@ erpnext.pos.PointOfSale = class PointOfSale {
]);
}
select_batch_and_serial_no(item) {
select_batch_and_serial_no(row) {
frappe.dom.unfreeze();
erpnext.show_serial_batch_selector(this.frm, item, () => {
this.update_item_in_frm(item, 'qty', item.qty)
.then(() => {
// update cart
frappe.run_serially([
() => {
if (item.qty === 0) {
frappe.model.clear_doc(item.doctype, item.name);
}
},
() => this.update_cart_data(item)
]);
});
erpnext.show_serial_batch_selector(this.frm, row, () => {
this.frm.doc.items.forEach(item => {
this.update_item_in_frm(item, 'qty', item.qty)
.then(() => {
// update cart
frappe.run_serially([
() => {
if (item.qty === 0) {
frappe.model.clear_doc(item.doctype, item.name);
}
},
() => this.update_cart_data(item)
]);
});
})
}, () => {
this.on_close(item);
this.on_close(row);
}, true);
}
on_close(item) {
if (!this.cart.exists(item.item_code) && item.qty) {
if (!this.cart.exists(item.item_code, item.batch_no) && item.qty) {
frappe.model.clear_doc(item.doctype, item.name);
}
}
@ -492,6 +496,11 @@ erpnext.pos.PointOfSale = class PointOfSale {
//
// }).addClass('visible-xs');
this.page.add_menu_item(__("Form View"), function () {
frappe.model.sync(me.frm.doc);
frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name);
});
this.page.add_menu_item(__("POS Profile"), function () {
frappe.set_route('List', 'POS Profile');
});
@ -602,11 +611,15 @@ class POSCart {
this.customer_field.set_value("");
this.frm.msgbox = "";
let total_item_qty = 0.0;
this.frm.set_value("pos_total_qty",total_item_qty);
this.$discount_amount.find('input:text').val('');
this.wrapper.find('.grand-total-value').text(
format_currency(this.frm.doc.grand_total, this.frm.currency));
this.wrapper.find('.rounded-total-value').text(
format_currency(this.frm.doc.rounded_total, this.frm.currency));
this.$qty_total.find(".quantity-total").text(total_item_qty);
const customer = this.frm.doc.customer;
this.customer_field.set_value(customer);
@ -721,7 +734,7 @@ class POSCart {
total_item_qty += d.qty;
}
});
this.$qty_total.find('.quantity-total').text(total_item_qty)
this.$qty_total.find('.quantity-total').text(total_item_qty);
this.frm.set_value("pos_total_qty",total_item_qty);
}
@ -804,10 +817,11 @@ class POSCart {
this.numpad.reset_value();
} else {
const item_code = this.selected_item.attr('data-item-code');
const batch_no = this.selected_item.attr('data-batch-no');
const field = this.selected_item.active_field;
const value = this.numpad.get_value();
this.events.on_field_change(item_code, field, value);
this.events.on_field_change(item_code, field, value, batch_no);
}
}
@ -835,7 +849,7 @@ class POSCart {
add_item(item) {
this.$empty_state.hide();
if (this.exists(item.item_code)) {
if (this.exists(item.item_code, item.batch_no)) {
// update quantity
this.update_item(item);
} else if (flt(item.qty) > 0.0) {
@ -848,7 +862,10 @@ class POSCart {
}
update_item(item) {
const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`);
const item_selector = item.batch_no ?
`[data-batch-no="${item.batch_no}"]` : `[data-item-code="${item.item_code}"]`;
const $item = this.$cart_items.find(item_selector);
if(item.qty > 0) {
const is_stock_item = this.get_item_details(item.item_code).is_stock_item;
@ -870,7 +887,8 @@ class POSCart {
const rate = format_currency(item.rate, this.frm.doc.currency);
const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red';
return `
<div class="list-item indicator ${indicator_class}" data-item-code="${item.item_code}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
<div class="list-item indicator ${indicator_class}" data-item-code="${item.item_code}"
data-batch-no="${item.batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
<div class="item-name list-item__content list-item__content--flex-1.5 ellipsis">
${item.item_name}
</div>
@ -911,8 +929,11 @@ class POSCart {
return this.item_data[item_code];
}
exists(item_code) {
let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
exists(item_code, batch_no) {
const is_exists = batch_no ?
`[data-batch-no="${batch_no}"]` : `[data-item-code="${item_code}"]`;
let $item = this.$cart_items.find(is_exists);
return $item.length > 0;
}

View File

@ -88,7 +88,7 @@ erpnext.stock.ItemDashboard = Class.extend({
if(!data) data = [];
data.forEach(function(d) {
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production;
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
d.pending_qty = 0;
d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
if(d.actual_or_pending > d.actual_qty) {

View File

@ -116,14 +116,14 @@ class Bin(Document):
se.docstatus=1
and se.purpose='Subcontract'
and ifnull(se.purchase_order, '') !=''
and sed.item_code = %s
and (sed.item_code = %(item)s or sed.original_item = %(item)s)
and se.name = sed.parent
and se.purchase_order = po.name
and po.docstatus = 1
and po.is_subcontracted = 'Yes'
and po.status != 'Closed'
and po.per_received < 100
""", (self.item_code))[0][0]
""", {'item': self.item_code})[0][0]
if reserved_qty_for_sub_contract > materials_transferred:
reserved_qty_for_sub_contract = reserved_qty_for_sub_contract - materials_transferred

View File

@ -456,7 +456,8 @@ $.extend(erpnext.item, {
],
fields: ["attribute_value"],
limit_start: 0,
limit_page_length: 500
limit_page_length: 500,
parent: "Item"
}
}).then((r) => {
if(r.message) {
@ -598,7 +599,8 @@ $.extend(erpnext.item, {
["parent","=", i],
["attribute_value", "like", term + "%"]
],
fields: ["attribute_value"]
fields: ["attribute_value"],
parent: "Item"
},
callback: function(r) {
if (r.message) {

View File

@ -366,6 +366,37 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Alternative Item",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -3686,7 +3717,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2018-03-09 03:13:18.516087",
"modified": "2018-03-12 03:13:18.516087",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -15,7 +15,7 @@ def get_data():
'transactions': [
{
'label': _('Groups'),
'items': ['BOM', 'Product Bundle']
'items': ['BOM', 'Product Bundle', 'Item Alternative']
},
{
'label': _('Pricing'),

View File

@ -0,0 +1,14 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Item Alternative', {
setup: function(frm) {
frm.fields_dict.item_code.get_query = () => {
return {
filters: {
'allow_alternative_item': 1
}
};
};
}
});

View File

@ -0,0 +1,292 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-02-26 17:39:11.249778",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item Code",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "alternative_item_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Alternative Item Code",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "two_way",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Two-way",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Name",
"length": 0,
"no_copy": 0,
"options": "item_code.item_name",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "alternative_item_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Alternative Item Name",
"length": 0,
"no_copy": 0,
"options": "alternative_item_code.item_name",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-03-07 16:08:08.097107",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Alternative",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Item Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "item_code",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
class ItemAlternative(Document):
def validate(self):
self.has_alternative_item()
self.validate_alternative_item()
self.validate_duplicate()
def has_alternative_item(self):
if (self.item_code and
not frappe.db.get_value('Item', self.item_code, 'allow_alternative_item')):
frappe.throw(_("Not allow to set alternative item for the item {0}").format(self.item_code))
def validate_alternative_item(self):
if self.item_code == self.alternative_item_code:
frappe.throw(_("Alternative item must not be same as item code"))
def validate_duplicate(self):
if frappe.db.get_value("Item Alternative", {'item_code': self.item_code,
'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}):
frappe.throw(_("Already record exists for the item {0}".format(self.item_code)))
def get_alternative_items(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" (select alternative_item_code from `tabItem Alternative`
where item_code = %(item_code)s and alternative_item_code like %(txt)s)
union
(select item_code from `tabItem Alternative`
where alternative_item_code = %(item_code)s and item_code like %(txt)s
and two_way = 1) limit {0}, {1}
""".format(start, page_len), {
"item_code": frappe.db.escape(filters.get('item_code')),
"txt": "%%%s%%" % frappe.db.escape(txt)
})

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Item Alternative", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Item Alternative
() => frappe.tests.make('Item Alternative', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe, json
from frappe.utils import flt
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry
import unittest
class TestItemAlternative(unittest.TestCase):
def setUp(self):
make_items()
def test_alternative_item_for_subcontract_rm(self):
create_stock_reconciliation(item_code='Alternate Item For A RW 1', warehouse='_Test Warehouse - _TC',
qty=5, rate=2000)
create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC',
qty=5, rate=2000)
supplier_warehouse = "Test Supplier Warehouse - _TC"
po = create_purchase_order(item = "Test Finished Goods - A",
is_subcontracted='Yes', qty=5, rate=3000, supplier_warehouse=supplier_warehouse)
rm_item = [{"item_code": "Test Finished Goods - A", "rm_item_code": "Test FG A RW 1", "item_name":"Test FG A RW 1",
"qty":5, "warehouse":"_Test Warehouse - _TC", "rate":2000, "amount":10000, "stock_uom":"Nos"},
{"item_code": "Test Finished Goods - A", "rm_item_code": "Test FG A RW 2", "item_name":"Test FG A RW 2",
"qty":5, "warehouse":"_Test Warehouse - _TC", "rate":2000, "amount":10000, "stock_uom":"Nos"}]
rm_item_string = json.dumps(rm_item)
reserved_qty_for_sub_contract = frappe.db.get_value('Bin',
{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_sub_contract')
se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
se.to_warehouse = supplier_warehouse
se.insert()
doc = frappe.get_doc('Stock Entry', se.name)
for item in doc.items:
if item.item_code == 'Test FG A RW 1':
item.item_code = 'Alternate Item For A RW 1'
item.item_name = 'Alternate Item For A RW 1'
item.description = 'Alternate Item For A RW 1'
item.original_item = 'Test FG A RW 1'
doc.save()
doc.submit()
after_transfer_reserved_qty_for_sub_contract = frappe.db.get_value('Bin',
{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_sub_contract')
self.assertEqual(after_transfer_reserved_qty_for_sub_contract, flt(reserved_qty_for_sub_contract - 5))
pr = make_purchase_receipt(po.name)
pr.save()
pr = frappe.get_doc('Purchase Receipt', pr.name)
status = False
for d in pr.supplied_items:
if d.rm_item_code == 'Alternate Item For A RW 1':
status = True
self.assertEqual(status, True)
def test_alternative_item_for_production_rm(self):
create_stock_reconciliation(item_code='Alternate Item For A RW 1',
warehouse='_Test Warehouse - _TC',qty=5, rate=2000)
create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC',
qty=5, rate=2000)
pro_order = make_wo_order_test_record(production_item='Test Finished Goods - A',
qty=5, source_warehouse='_Test Warehouse - _TC', wip_warehouse='Test Supplier Warehouse - _TC')
reserved_qty_for_production = frappe.db.get_value('Bin',
{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_production')
ste = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 5))
ste.insert()
for item in ste.items:
if item.item_code == 'Test FG A RW 1':
item.item_code = 'Alternate Item For A RW 1'
item.item_name = 'Alternate Item For A RW 1'
item.description = 'Alternate Item For A RW 1'
item.original_item = 'Test FG A RW 1'
ste.submit()
reserved_qty_for_production_after_transfer = frappe.db.get_value('Bin',
{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_production')
self.assertEqual(reserved_qty_for_production_after_transfer, flt(reserved_qty_for_production - 5))
ste1 = frappe.get_doc(make_stock_entry(pro_order.name, "Manufacture", 5))
status = False
for d in ste1.items:
if d.item_code == 'Alternate Item For A RW 1':
status = True
self.assertEqual(status, True)
ste1.submit()
def make_items():
items = ['Test Finished Goods - A', 'Test FG A RW 1', 'Test FG A RW 2', 'Alternate Item For A RW 1']
for item_code in items:
if not frappe.db.exists('Item', item_code):
create_item(item_code)
create_stock_reconciliation(item_code="Test FG A RW 1",
warehouse='_Test Warehouse - _TC', qty=10, rate=2000)
if frappe.db.exists('Item', 'Test FG A RW 1'):
doc = frappe.get_doc('Item', 'Test FG A RW 1')
doc.allow_alternative_item = 1
doc.save()
if frappe.db.exists('Item', 'Test Finished Goods - A'):
doc = frappe.get_doc('Item', 'Test Finished Goods - A')
doc.is_sub_contracted_item = 1
doc.save()
if not frappe.db.get_value('BOM',
{'item': 'Test Finished Goods - A', 'docstatus': 1}):
make_bom(item = 'Test Finished Goods - A', raw_materials = ['Test FG A RW 1', 'Test FG A RW 2'])
if not frappe.db.get_value('Warehouse', {'warehouse_name': 'Test Supplier Warehouse'}):
frappe.get_doc({
'doctype': 'Warehouse',
'warehouse_name': 'Test Supplier Warehouse',
'company': '_Test Company'
}).insert(ignore_permissions=True)

View File

@ -120,6 +120,25 @@ frappe.ui.form.on('Stock Entry', {
});
}
if(frm.doc.items) {
const has_alternative = frm.doc.items.find(i => i.allow_alternative_item === 1);
if (frm.doc.docstatus == 0 && has_alternative) {
frm.add_custom_button(__('Alternate Item'), () => {
erpnext.utils.select_alternate_items({
frm: frm,
child_docname: "items",
warehouse_field: "s_warehouse",
child_doctype: "Stock Entry Detail",
original_item_field: "original_item",
condition: (d) => {
if (d.s_warehouse && d.allow_alternative_item) {return true;}
}
})
});
}
}
if (frm.doc.docstatus===0) {
frm.add_custom_button(__('Purchase Invoice'), function() {
erpnext.utils.map_current_doc({

View File

@ -400,9 +400,10 @@ class StockEntry(StockController):
if self.purpose == "Subcontract" and self.purchase_order:
purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
for se_item in self.items:
item_code = se_item.original_item or se_item.item_code
precision = cint(frappe.db.get_default("float_precision")) or 3
total_allowed = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
if d.rm_item_code == se_item.item_code])
if d.rm_item_code == item_code])
if not total_allowed:
frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}")
.format(se_item.item_code, self.purchase_order))
@ -421,7 +422,8 @@ class StockEntry(StockController):
def validate_bom(self):
for d in self.get('items'):
if d.bom_no and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
validate_bom_no(d.item_code, d.bom_no)
item_code = d.original_item or d.item_code
validate_bom_no(item_code, d.bom_no)
def validate_finished_goods(self):
"""validation: finished good quantity should be same as manufacturing quantity"""
@ -608,7 +610,6 @@ class StockEntry(StockController):
item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
#Get PO Supplied Items Details
print('Purchase Order', self.purchase_order, self.purpose)
if self.purchase_order and self.purpose == "Subcontract":
#Get PO Supplied Items Details
item_wh = frappe._dict(frappe.db.sql("""
@ -695,9 +696,23 @@ class StockEntry(StockController):
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
fetch_exploded = self.use_multi_level_bom)
used_alternative_items = get_used_alternative_items(work_order = self.work_order)
for item in item_dict.values():
# if source warehouse presents in BOM set from_warehouse as bom source_warehouse
if item["allow_alternative_item"]:
item["allow_alternative_item"] = frappe.db.get_value('Work Order',
self.work_order, "allow_alternative_item")
item.from_warehouse = self.from_warehouse or item.source_warehouse or item.default_warehouse
if item.item_code in used_alternative_items:
alternative_item_data = used_alternative_items.get(item.item_code)
item.item_code = alternative_item_data.item_code
item.item_name = alternative_item_data.item_name
item.stock_uom = alternative_item_data.stock_uom
item.uom = alternative_item_data.uom
item.conversion_factor = alternative_item_data.conversion_factor
item.description = alternative_item_data.description
return item_dict
def get_bom_scrap_material(self, qty):
@ -805,16 +820,19 @@ class StockEntry(StockController):
wip_warehouse = pro_order.wip_warehouse
else:
wip_warehouse = None
for d in pro_order.get("required_items"):
if flt(d.required_qty) > flt(d.transferred_qty):
item_row = d.as_dict()
if d.source_warehouse and not frappe.db.get_value("Warehouse", d.source_warehouse, "is_group"):
item_row["from_warehouse"] = d.source_warehouse
item_row["to_warehouse"] = wip_warehouse
if item_row["allow_alternative_item"]:
item_row["allow_alternative_item"] = pro_order.allow_alternative_item
item_dict.setdefault(d.item_code, item_row)
return item_dict
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
@ -827,7 +845,7 @@ class StockEntry(StockController):
se_child = self.append('items')
se_child.s_warehouse = item_dict[d].get("from_warehouse")
se_child.t_warehouse = item_dict[d].get("to_warehouse")
se_child.item_code = cstr(d)
se_child.item_code = item_dict[d].get('item_code') or cstr(d)
se_child.item_name = item_dict[d]["item_name"]
se_child.description = item_dict[d]["description"]
se_child.uom = stock_uom
@ -835,6 +853,7 @@ class StockEntry(StockController):
se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
se_child.expense_account = item_dict[d].get("expense_account") or expense_account
se_child.cost_center = item_dict[d].get("cost_center") or cost_center
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
if item_dict[d].get("idx"):
se_child.idx = item_dict[d].get("idx")
@ -882,8 +901,9 @@ class StockEntry(StockController):
#Update reserved sub contracted quantity in bin based on Supplied Item Details
for d in self.get("items"):
reserve_warehouse = item_wh.get(d.item_code)
stock_bin = get_bin(d.item_code, reserve_warehouse)
item_code = d.get('original_item') or d.get('item_code')
reserve_warehouse = item_wh.get(item_code)
stock_bin = get_bin(item_code, reserve_warehouse)
stock_bin.update_reserved_qty_for_sub_contracting()
@frappe.whitelist()
@ -976,6 +996,30 @@ def get_operating_cost_per_unit(work_order=None, bom_no=None):
return operating_cost_per_unit
def get_used_alternative_items(purchase_order=None, work_order=None):
cond = ""
if purchase_order:
cond = "and ste.purpose = 'Subcontract' and ste.purchase_order = '{0}'".format(purchase_order)
elif work_order:
cond = "and ste.purpose = 'Material Transfer for Manufacture' and ste.work_order = '{0}'".format(work_order)
if not cond: return {}
used_alternative_items = {}
data = frappe.db.sql(""" select sted.original_item, sted.uom, sted.conversion_factor,
sted.item_code, sted.item_name, sted.conversion_factor,sted.stock_uom, sted.description
from
`tabStock Entry` ste, `tabStock Entry Detail` sted
where
sted.parent = ste.name and ste.docstatus = 1 and sted.original_item != sted.item_code
{0} """.format(cond), as_dict=1)
for d in data:
used_alternative_items[d.original_item] = d
return used_alternative_items
@frappe.whitelist()
def get_uom_details(item_code, uom, qty):
"""Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}`

View File

@ -41,6 +41,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -70,6 +71,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -102,6 +104,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -130,6 +133,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -162,6 +166,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -190,6 +195,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -222,6 +228,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -250,6 +257,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -279,6 +287,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -309,6 +318,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -341,6 +351,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "300px"
},
@ -371,6 +382,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -401,6 +413,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -432,6 +445,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -461,6 +475,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -492,6 +507,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -524,6 +540,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -555,6 +572,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -586,6 +604,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -618,6 +637,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -649,6 +669,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -677,6 +698,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -709,6 +731,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -740,6 +763,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -772,6 +796,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -803,6 +828,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -834,6 +860,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -866,6 +893,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -895,6 +923,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -926,6 +955,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -954,6 +984,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -986,6 +1017,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1018,6 +1050,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1047,6 +1080,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1078,6 +1112,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1106,6 +1141,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1138,6 +1174,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1167,6 +1204,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1197,6 +1235,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1228,6 +1267,7 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1259,6 +1299,39 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "s_warehouse",
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Alternative Item",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1287,6 +1360,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1318,6 +1392,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1348,6 +1423,39 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "original_item",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Original Item",
"length": 0,
"no_copy": 1,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -1361,7 +1469,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-02-16 20:19:57.471380",
"modified": "2018-03-05 13:09:25.849700",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Detail",

View File

@ -320,7 +320,7 @@ def get_price_list_rate(args, item_doc, out):
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \
/ flt(args.conversion_rate)
if not args.price_list_uom_dependant:
out.price_list_rate = flt(out.price_list_rate * (args.conversion_factor or 1.0))
out.price_list_rate = flt(out.price_list_rate * (flt(args.conversion_factor) or 1.0))
if not out.price_list_rate and args.transaction_type=="buying":
from erpnext.stock.doctype.item.item import get_last_purchase_details

View File

@ -14,10 +14,15 @@ def get_data(item):
if not item:
return []
item_dicts = []
variants = None
variant_results = frappe.db.sql("""select name from `tabItem`
where variant_of = %s""", item, as_dict=1)
variants = ",".join(['"' + frappe.db.escape(variant['name']) + '"' for variant in variant_results])
if not variant_results:
frappe.msgprint(_("There isn't any item variant for the selected item"))
return []
else:
variants = ",".join(['"' + frappe.db.escape(variant['name']) + '"' for variant in variant_results])
order_count_map = get_open_sales_orders_map(variants)
stock_details_map = get_stock_details_map(variants)