Merge branch 'develop' into issue-webview-fixed

This commit is contained in:
Nabin Hait 2017-03-23 17:00:30 +05:30 committed by GitHub
commit 691a34c924
24 changed files with 484 additions and 254 deletions

View File

@ -0,0 +1,3 @@
frappe.listview_settings['Asset'] = {
add_fields: ['image']
}

View File

@ -12,19 +12,42 @@ class InvalidItemAttributeValueError(frappe.ValidationError): pass
class ItemTemplateCannotHaveStock(frappe.ValidationError): pass
@frappe.whitelist()
def get_variant(template, args, variant=None):
"""Validates Attributes and their Values, then looks for an exactly matching Item Variant
def get_variant(template, args=None, variant=None, manufacturer=None,
manufacturer_part_no=None):
"""Validates Attributes and their Values, then looks for an exactly
matching Item Variant
:param item: Template Item
:param args: A dictionary with "Attribute" as key and "Attribute Value" as value
"""
if isinstance(args, basestring):
args = json.loads(args)
item_template = frappe.get_doc('Item', template)
if not args:
frappe.throw(_("Please specify at least one attribute in the Attributes table"))
if item_template.variant_based_on=='Manufacturer' and manufacturer:
return make_variant_based_on_manufacturer(item_template, manufacturer,
manufacturer_part_no)
else:
if isinstance(args, basestring):
args = json.loads(args)
return find_variant(template, args, variant)
if not args:
frappe.throw(_("Please specify at least one attribute in the Attributes table"))
return find_variant(template, args, variant)
def make_variant_based_on_manufacturer(template, manufacturer, manufacturer_part_no):
'''Make and return a new variant based on manufacturer and
manufacturer part no'''
from frappe.model.naming import append_number_if_name_exists
variant = frappe.new_doc('Item')
copy_attributes_to_variant(template, variant)
variant.manufacturer = manufacturer
variant.manufacturer_part_no = manufacturer_part_no
variant.item_code = append_number_if_name_exists('Item', template.name)
return variant
def validate_item_variant_attributes(item, args=None):
if isinstance(item, basestring):
@ -131,6 +154,7 @@ def create_variant(item, args):
template = frappe.get_doc("Item", item)
variant = frappe.new_doc("Item")
variant.variant_based_on = 'Item Attribute'
variant_attributes = []
for d in template.attributes:
@ -147,17 +171,28 @@ def create_variant(item, args):
def copy_attributes_to_variant(item, variant):
from frappe.model import no_value_fields
# copy non no-copy fields
exclude_fields = ["item_code", "item_name", "show_in_website"]
if item.variant_based_on=='Manufacturer':
# don't copy manufacturer values if based on part no
exclude_fields += ['manufacturer', 'manufacturer_part_no']
for field in item.meta.fields:
if field.fieldtype not in no_value_fields and (not field.no_copy)\
and field.fieldname not in ("item_code", "item_name", "show_in_website"):
and field.fieldname not in exclude_fields:
if variant.get(field.fieldname) != item.get(field.fieldname):
variant.set(field.fieldname, item.get(field.fieldname))
variant.variant_of = item.name
variant.has_variants = 0
if variant.attributes:
variant.description += "\n"
for d in variant.attributes:
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
if item.variant_based_on=='Item Attribute':
if variant.attributes:
variant.description += "\n"
for d in variant.attributes:
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
def make_variant_item_code(template_item_code, variant):
"""Uses template's item code and abbreviations to make variant's item code"""

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -12,7 +12,7 @@ attitude.
Like exercise.
Human body may seem like it does not require exercise today or even tomorrow,
The human body may seem like it does not require exercise today or even tomorrow,
but in the long run, if you wish to maintain your body and its health, you
should get on the treadmill.

View File

@ -4,9 +4,9 @@ ERPNext tracks the multiple permissions before submission.
Example of a leave application workflow is given below:
If an user applies for a leave, then his request will be sent to the HR
department. The HR department(HR User) will either reject or approve this
request. Once this process is completed, the user's Manager(leave approver)
If a user applies for a leave, then his request will be sent to the HR
department. The HR department (HR User) will either reject or approve this
request. Once this process is completed, the user's Manager (leave approver)
will get an indication that the HR department has Accepted or Rejected. The
Manager, who is the approving authority, will either Approve or Reject this
request. Accordingly,the user will get his Approved or Rejected status.
@ -44,23 +44,17 @@ workflow transition step that says from submitted you can cancel.
#### Example of a Leave Application Process:
Go to the Human Resources Module and click on Leave Application. Apply for a
Leave.
When a Leave Application is saved by Employee, the status of the document changes to "Applied"
When a Leave Application is submitted, the status on the right hand corner of
the page shows as "Applied"
![Workflow Employee LA]({{docs_base_url}}/assets/old_images/erpnext/workflow-employee-la.png)
![Workflow Employee LA]({{docs_base_url}}/assets/img/setup/workflow-3.png)
When the HR User logs in, he can either Approve or Reject. If approved the
status on the right hand corner of the page shows as Approved. However, a blue
band of information is displayed saying approval is pending by leave approver.
status of the document changes to "Approved by HR". However, it is yet to be approved by Leave Approver.
![Leave Approver]({{docs_base_url}}/assets/old_images/erpnext/workflow-hr-user-la.png)
![Leave Approver]({{docs_base_url}}/assets/img/setup/workflow-4.png)
When the leave approver opens the Leave Application page, he should select the
status and convert to Approved or Rejected.
When the Leave Approver opens the Leave Application page, he can finally "Approve" or "Reject" the Leave Application.
![Workflow Leave Approver]({{docs_base_url}}/assets/old_images/erpnext/workflow-leave-approver-la.png)
![Workflow Leave Approver]({{docs_base_url}}/assets/img/setup/workflow-5.png)
{next}

View File

@ -1,15 +1,28 @@
# Item Variants
### What are Variants?
A Item Variant is a version of a Item, such as differing sizes or differing colours (like a _blue_ t-shirt in size _small_ rather then just a t-shirt).
Without Item variants, you would have to treat the _small, medium_ and _large_ versions of a t-shirt as three separate Items;
Without Item variants, you would have to treat the _small, medium_ and _large_ versions of a t-shirt as three separate Items;
Item variants let you treat the _small, medium_ and _large_ versions of a t-shirt as variations of the one Item 't-shirt'.
### Using Variants
Variants can be based on two things
1. Item Attributes
1. Manufacturers
### Variants Based on Item Attributes
To use Item Variants in ERPNext, create an Item and check 'Has Variants'.
* The Item shall then be referred to as a so called 'Template'. Such a Template is not identical to a regular 'Item' any longer. For example it (the Template) can not be used directly in any Transactions (Sales Order, Delivery Note, Purchase Invoice) itself. Only the Variants of an Item (_blue_ t-shirt in size _small)_ can be practically used in such. Therefore it would be ideal to decide whether an item 'Has Variants' or not directly when creating it.
* The Item shall then be referred to as a so called 'Template'. Such a Template is not identical to a regular 'Item' any longer. For example it (the Template) can not be used directly in any Transactions (Sales Order, Delivery Note, Purchase Invoice) itself. Only the Variants of an Item (_blue_ t-shirt in size _small)_ can be practically used in such. Therefore it would be ideal to decide whether an item 'Has Variants' or not directly when creating it.
<img class="screenshot" alt="Has Variants" src="{{docs_base_url}}/assets/img/stock/item-has-variants.png">
On selecting 'Has Variants' a table shall appear. Specify the variant attributes for the Item in the table.
In case the attribute has Numeric Values, you can specify the range and increment values here.
In case the attribute has Numeric Values, you can specify the range and increment values here.
<img class="screenshot" alt="Valid Attributes" src="{{docs_base_url}}/assets/img/stock/item-attributes.png">
@ -22,3 +35,17 @@ To create 'Item Variants' against a 'Template' select 'Make Variants'
<img class="screenshot" alt="Make Variants" src="{{docs_base_url}}/assets/img/stock/make-variant-1.png">
To learn more about setting Attributes Master check [Item Attributes]({{docs_base_url}}/user/manual/en/stock/setup/item-attribute.html)
### Variants Based on Manufacturers
To setup variants based on Manufactueres, in your Item template, set "Variants Based On" as "Manufacturers"
<img class='screenshot' alt='Setup Item Variant by Manufacturer'
src='{{docs_base_url}}/assets/img/stock/select-mfg-for-variant.png'>
When you make a new Variant, the system will prompt you to select a Manufacturer. You can also optionally put in a Manufacturer Part Number
<img class='screenshot' alt='Setup Item Variant by Manufacturer'
src='{{docs_base_url}}/assets/img/stock/set-variant-by-mfg.png'>
The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1"

View File

@ -1,201 +1,228 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:route",
"beta": 0,
"creation": "2013-01-15 16:13:36",
"custom": 0,
"description": "Description of a Job Opening",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"engine": "InnoDB",
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:route",
"beta": 0,
"creation": "2013-01-15 16:13:36",
"custom": 0,
"description": "Description of a Job Opening",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "job_title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Job Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "job_title",
"fieldtype": "Data",
"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": "Job Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "publish",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Publish on website",
"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,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "publish",
"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": "Publish on website",
"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,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "publish",
"fieldname": "route",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Route",
"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,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "publish",
"fieldname": "route",
"fieldtype": "Data",
"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": "Route",
"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,
"unique": 1
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nClosed",
"permlevel": 0,
"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,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nClosed",
"permlevel": 0,
"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,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Job profile, qualifications required etc.",
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Job profile, qualifications required etc.",
"fieldname": "description",
"fieldtype": "Text Editor",
"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": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-bookmark",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-19 05:54:38.298496",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Opening",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-bookmark",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-03-22 12:36:26.807200",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Opening",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Guest",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_order": "ASC",
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 0,
"track_seen": 0
}

View File

@ -148,10 +148,9 @@ class SalarySlip(TransactionBase):
})
def get_date_details(self):
if not self.end_date:
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
self.start_date = date_details.start_date
self.end_date = date_details.end_date
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
self.start_date = date_details.start_date
self.end_date = date_details.end_date
def check_sal_struct(self, joining_date, relieving_date):
cond = ''

View File

@ -327,4 +327,3 @@ body[data-route="pos"] .btn-more {
body[data-route="pos"] .collapse-btn {
cursor: pointer;
}

View File

@ -177,11 +177,11 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
fields: [
{fieldtype:'Read Only', fieldname:'item_code',
label: __('Item Code'), in_list_view:1},
{fieldtype:'Link', fieldname:'bom', options: 'BOM',
{fieldtype:'Link', fieldname:'bom', options: 'BOM', reqd: 1,
label: __('Select BOM'), in_list_view:1, get_query: function(doc) {
return {filters: {item: doc.item_code}};
}},
{fieldtype:'Float', fieldname:'pending_qty',
{fieldtype:'Float', fieldname:'pending_qty', reqd: 1,
label: __('Qty'), in_list_view:1},
],
get_data: function() {

View File

@ -31,10 +31,14 @@ def get_cart_quotation(doc=None):
doc = quotation
set_cart_count(quotation)
addresses = get_address_docs(party=party)
return {
"doc": decorate_quotation_doc(doc),
"addresses": [{"name": address.name, "display": address.display}
for address in get_address_docs(party=party)],
"shipping_addresses": [{"name": address.name, "display": address.display}
for address in addresses if address.address_type == "Shipping"],
"billing_addresses": [{"name": address.name, "display": address.display}
for address in addresses if address.address_type == "Billing"],
"shipping_rules": get_applicable_shipping_rules(party)
}

View File

@ -261,6 +261,44 @@ $.extend(erpnext.item, {
make_variant: function(frm) {
var fields = []
if(frm.doc.variant_based_on==="Item Attribute") {
erpnext.item.show_modal_for_item_attribute_selection(frm);
} else {
erpnext.item.show_modal_for_manufacturers(frm);
}
},
show_modal_for_manufacturers: function(frm) {
var dialog = new frappe.ui.Dialog({
fields: [
{fieldtype:'Link', options:'Manufacturer',
reqd:1, label:'Manufacturer'},
{fieldtype:'Data', label:'Manufacturer Part Number',
fieldname: 'manufacturer_part_no'},
]
});
dialog.set_primary_action(__('Make'), function() {
var data = dialog.get_values();
if(!data) return;
// call the server to make the variant
data.template = frm.doc.name;
frappe.call({
method:"erpnext.controllers.item_variant.get_variant",
args: data,
callback: function(r) {
var doclist = frappe.model.sync(r.message);
dialog.hide();
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
})
dialog.show();
},
show_modal_for_item_attribute_selection: function(frm) {
for(var i=0;i< frm.doc.attributes.length;i++){
var fieldtype, desc;
var row = frm.doc.attributes[i];
@ -371,13 +409,42 @@ $.extend(erpnext.item, {
})
});
},
toggle_attributes: function(frm) {
frm.toggle_display("attributes", frm.doc.has_variants || frm.doc.variant_of);
frm.fields_dict.attributes.grid.toggle_reqd("attribute_value", frm.doc.variant_of ? 1 : 0);
frm.fields_dict.attributes.grid.set_column_disp("attribute_value", frm.doc.variant_of ? 1 : 0);
frm.toggle_enable("attributes", !frm.doc.variant_of);
frm.fields_dict.attributes.grid.toggle_enable("attribute", !frm.doc.variant_of);
frm.fields_dict.attributes.grid.toggle_enable("attribute_value", !frm.doc.variant_of);
toggle_attributes: function(frm) {
if((frm.doc.has_variants || frm.doc.variant_of)
&& frm.doc.variant_based_on==='Item Attribute') {
frm.toggle_display("attributes", true);
var grid = frm.fields_dict.attributes.grid;
if(frm.doc.variant_of) {
// variant
// value column is displayed but not editable
grid.set_column_disp("attribute_value", true);
grid.toggle_enable("attribute_value", false);
grid.toggle_enable("attribute", false);
// can't change attributes since they are
// saved when the variant was created
frm.toggle_enable("attributes", false);
} else {
// template - values not required!
// make the grid editable
frm.toggle_enable("attributes", true);
// value column is hidden
grid.set_column_disp("attribute_value", false);
// enable the grid so you can add more attributes
grid.toggle_enable("attribute", true);
}
} else {
// nothing to do with attributes, hide it
frm.toggle_display("attributes", false);
}
}
});

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:item_code",
@ -1218,7 +1219,39 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"default": "Item Attribute",
"depends_on": "has_variants",
"fieldname": "variant_based_on",
"fieldtype": "Select",
"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": "Variant Based On",
"length": 0,
"no_copy": 0,
"options": "Item Attribute\nManufacturer",
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.has_variants && doc.variant_based_on==='Item Attribute'",
"fieldname": "attributes",
"fieldtype": "Table",
"hidden": 1,
@ -2792,6 +2825,7 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-tag",
@ -2799,12 +2833,11 @@
"image_field": "image",
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2017-02-20 13:26:45.446617",
"modified": "2017-03-21 21:03:10.715674",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -643,7 +643,7 @@ class Item(WebsiteGenerator):
.format(self.stock_uom, template_uom))
def validate_attributes(self):
if self.has_variants or self.variant_of:
if (self.has_variants or self.variant_of) and self.variant_based_on=='Item Attribute':
attributes = []
if not self.attributes:
frappe.throw(_("Attribute table is mandatory"))
@ -654,7 +654,7 @@ class Item(WebsiteGenerator):
attributes.append(d.attribute)
def validate_variant_attributes(self):
if self.variant_of:
if self.variant_of and self.variant_based_on=='Item Attribute':
args = {}
for d in self.attributes:
if not d.attribute_value:
@ -675,7 +675,7 @@ def get_timeline_data(doctype, name):
from `tabStock Ledger Entry` where item_code=%s
and posting_date > date_sub(curdate(), interval 1 year)
group by posting_date''', name))
for date, count in items.iteritems():
timestamp = get_timestamp(date)
out.update({ timestamp: count })

View File

@ -7,7 +7,7 @@ import frappe
from frappe.test_runner import make_test_records
from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError,
InvalidItemAttributeValueError)
InvalidItemAttributeValueError, get_variant)
from frappe.model.rename_doc import rename_doc
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@ -167,31 +167,66 @@ class TestItem(unittest.TestCase):
variant.item_name = "_Test Numeric Variant Large 1.1m"
self.assertRaises(InvalidItemAttributeValueError, variant.save)
variant = create_variant("_Test Numeric Template Item",
variant = create_variant("_Test Numeric Template Item",
{"Test Size": "Large", "Test Item Length": 1.5})
self.assertEquals(variant.item_code, "_Test Numeric Template Item-L-1.5")
variant.item_code = "_Test Numeric Variant-L-1.5"
variant.item_name = "_Test Numeric Variant Large 1.5m"
variant.save()
def test_item_merging(self):
def test_item_merging(self):
create_item("Test Item for Merging 1")
create_item("Test Item for Merging 2")
make_stock_entry(item_code="Test Item for Merging 1", target="_Test Warehouse - _TC",
make_stock_entry(item_code="Test Item for Merging 1", target="_Test Warehouse - _TC",
qty=1, rate=100)
make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC",
make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC",
qty=1, rate=100)
rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True)
self.assertFalse(frappe.db.exists("Item", "Test Item for Merging 1"))
self.assertTrue(frappe.db.get_value("Bin",
self.assertTrue(frappe.db.get_value("Bin",
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse - _TC"}))
self.assertTrue(frappe.db.get_value("Bin",
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"}))
self.assertTrue(frappe.db.get_value("Bin",
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"}))
def test_item_variant_by_manufacturer(self):
if frappe.db.exists('Item', '_Test Variant Mfg'):
frappe.delete_doc('Item', '_Test Variant Mfg')
if frappe.db.exists('Item', '_Test Variant Mfg-1'):
frappe.delete_doc('Item', '_Test Variant Mfg-1')
if frappe.db.exists('Manufacturer', 'MSG1'):
frappe.delete_doc('Manufacturer', 'MSG1')
template = frappe.get_doc(dict(
doctype='Item',
item_code='_Test Variant Mfg',
has_variant=1,
item_group='Products',
variant_based_on='Manufacturer'
)).insert()
manufacturer = frappe.get_doc(dict(
doctype='Manufacturer',
short_name='MSG1'
)).insert()
variant = get_variant(template.name, manufacturer=manufacturer.name)
self.assertEquals(variant.item_code, '_Test Variant Mfg-1')
self.assertEquals(variant.description, '_Test Variant Mfg')
self.assertEquals(variant.manufacturer, 'MSG1')
variant.insert()
variant = get_variant(template.name, manufacturer=manufacturer.name,
manufacturer_part_no='007')
self.assertEquals(variant.item_code, '_Test Variant Mfg-2')
self.assertEquals(variant.description, '_Test Variant Mfg')
self.assertEquals(variant.manufacturer, 'MSG1')
self.assertEquals(variant.manufacturer_part_no, '007')
def make_item_variant():
if not frappe.db.exists("Item", "_Test Variant Item-S"):
@ -215,6 +250,5 @@ def create_item(item_code, is_stock_item=None):
item.item_name = item_code
item.description = item_code
item.item_group = "All Item Groups"
item.is_stock_item = is_stock_item or 1
item.is_stock_item = is_stock_item or 1
item.save()

View File

@ -63,7 +63,7 @@ def get_list_context(context=None):
'no_breadcrumbs': True
}
def get_issue_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by = None):
def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by=None):
from frappe.www.list import get_list
user = frappe.session.user
ignore_permissions = False

View File

@ -24,9 +24,17 @@ $.extend(shopping_cart, {
if($(this).prop("checked")) {
var me = this;
// uncheck other shipping or billing addresses:
if ( $(this).is('input[data-fieldname=customer_address]') ) {
$('input[data-fieldname=customer_address]').not(this).prop('checked', false);
} else {
$('input[data-fieldname=shipping_address_name]').not(this).prop('checked', false);
}
return frappe.call({
type: "POST",
method: "erpnext.shopping_cart.cart.update_cart_address",
freeze: true,
args: {
address_fieldname: $(this).attr("data-fieldname"),
address_name: $(this).attr("data-address-name")

View File

@ -7,7 +7,7 @@
<div class="h6 text-uppercase">{{ _("Shipping Address") }}</div>
<div id="cart-shipping-address" class="panel-group"
data-fieldname="shipping_address_name">
{% for address in addresses %}
{% for address in shipping_addresses %}
{{ show_address(address, doc, "shipping_address_name", select_address) }}
{% endfor %}
</div>
@ -18,7 +18,7 @@
<div class="h6 text-uppercase">Billing Address</div>
<div id="cart-billing-address" class="panel-group"
data-fieldname="customer_address">
{% for address in addresses %}
{% for address in billing_addresses %}
{{ show_address(address, doc, "customer_address", select_address) }}
{% endfor %}
</div>