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

This commit is contained in:
Saurabh 2018-02-09 10:51:49 +05:30
commit c29f3c70dc
42 changed files with 5426 additions and 5885 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1177,8 +1177,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
$(this.wrapper).on("change", ".pos-item-disc", function () {
var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code");
var discount = $(this).val();
me.update_discount(item_code, discount)
me.update_value()
if(discount > 100){
discount = $(this).val('');
frappe.show_alert({
indicator: 'red',
message: __('Discount amount cannot be greater than 100%')
});
me.update_discount(item_code, discount);
}else{
me.update_discount(item_code, discount);
me.update_value();
}
})
},
@ -2014,4 +2023,4 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
frappe.throw(__("LocalStorage is full , did not save"))
}
}
})
})

View File

@ -1,3 +1,11 @@
<style>
@media screen {
.print-format {
padding: 8mm;
}
}
</style>
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h4 class="text-center">{%= filters.customer || filters.supplier %} </h4>
<h5 class="text-center">
@ -6,17 +14,90 @@
{%= dateutil.str_to_user(filters.report_date) %}
</h5>
<hr>
{% if(filters.show_pdc_in_print) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
var range2 = report.columns[12].label;
var range3 = report.columns[13].label;
var range4 = report.columns[14].label;
%}
{% if(balance_row) { %}
<table class="table table-bordered table-condensed table-sm small">
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
<colgroup>
<col style="width: 30mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
</colgroup>
<thead>
<tr>
<th>{%= __(" ") %}</th>
<th>{%= __(range1) %}</th>
<th>{%= __(range2) %}</th>
<th>{%= __(range3) %}</th>
<th>{%= __(range4) %}</th>
<th>{%= __("Total") %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{%= __("Total Outstanding") %}</td>
<td class="text-right">{%= format_currency(balance_row[range1]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range2]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range3]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range4]) %}</td>
<td class="text-right">
{%= format_currency(flt(balance_row[__("Outstanding Amount")]), data[data.length-1]["currency"]) %}
</td>
</tr>
<td>{%= __("PDC/LC") %}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-right">
{%= format_currency(flt(balance_row[__("PDC/LC Amount")]), data[data.length-1]["currency"]) %}
</td>
<tr class="cvs-footer">
<th class="text-left">{%= __("Cheques Required") %}</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="text-right">
{%= format_currency(flt(balance_row[__("Outstanding Amount")]-balance_row[__("PDC/LC Amount")]), data[data.length-1]["currency"]) %}</th>
</tr>
</tbody>
</table>
{% } %}
{% } %}
<table class="table table-bordered">
<thead>
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 14%">{%= __("Date") %}</th>
<th style="width: 16%">{%= __("Ref") %}</th>
<th style="width: 30%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 10%">{%= __("Ref") %}</th>
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 10%">{%= __("Invoiced Amount") %}</th>
<th style="width: 10%">{%= __("Paid Amount") %}</th>
<th style="width: 10%">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
<th style="width: 10%">{%= __("Outstanding Amount") %}</th>
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 10%">{%= __("Paid Amount") %}</th>
<th style="width: 10%">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 6%">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 6%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 6%">{%= __("PDC/LC Date") %}</th>
<th style="width: 6%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 6%">{%= __("PDC/LC Amount") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
@ -48,23 +129,49 @@
</td>
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= format_currency(data[i]["Paid Amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["Credit Note"], data[i]["currency"]) : format_currency(data[i]["Debit Note"], data[i]["currency"]) %}</td>
{% if(!filters.show_pdc_in_print) { %}
<td style="text-align: right">
{%= format_currency(data[i]["Paid Amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["Credit Note"], data[i]["currency"]) : format_currency(data[i]["Debit Note"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["Outstanding Amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i][__("Customer LPO")] %}</td>
{% } %}
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][__("PDC/LC Ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("PDC/LC Amount")], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td></td>
<td></td>
<td><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"] ) %}</td>
<td style="text-align: right">
{%= format_currency(data[i]["Paid Amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["Credit Note"], data[i]["currency"]) : format_currency(data[i]["Debit Note"], data[i]["currency"]) %} </td>
{% if(!filters.show_pdc_in_print) { %}
<td style="text-align: right">
{%= format_currency(data[i]["Paid Amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["Credit Note"], data[i]["currency"]) : format_currency(data[i]["Debit Note"], data[i]["currency"]) %} </td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["Outstanding Amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i][__("Customer LPO")] %}</td>
{% } %}
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][__("PDC/LC Ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("PDC/LC Amount")], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]|| "&nbsp;") { %}

View File

@ -82,6 +82,11 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
"fieldname":"show_pdc_in_print",
"label": __("Show PDC in Print"),
"fieldtype": "Check",
}
],

View File

@ -72,6 +72,18 @@ class ReceivablePayableReport(object):
"options": "Currency",
"width": 100
})
columns += [
_("PDC/LC Date") + ":Date:110",
_("PDC/LC Ref") + ":Data:110",
_("PDC/LC Amount") + ":Currency/currency:130",
_("Remaining Balance") + ":Currency/currency:130"
]
if args.get('party_type') == 'Customer':
columns += [_("Customer LPO") + ":Data:100"]
columns += [_("Delivery Note") + ":Data:100"]
if args.get("party_type") == "Customer":
columns += [
_("Territory") + ":Link/Territory:80",
@ -89,7 +101,8 @@ class ReceivablePayableReport(object):
currency_precision = get_currency_precision() or 2
dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
voucher_details = self.get_voucher_details(args.get("party_type"))
dn_details = get_dn_details(args.get("party_type"))
voucher_details = self.get_voucher_details(args.get("party_type"), dn_details)
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
@ -101,6 +114,8 @@ class ReceivablePayableReport(object):
return_entries = self.get_return_entries(args.get("party_type"))
data = []
pdc_details = get_pdc_details(args.get("party_type"))
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):
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
@ -144,6 +159,18 @@ class ReceivablePayableReport(object):
else:
row.append(company_currency)
pdc = pdc_details.get(gle.voucher_no, {})
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]
if args.get('party_type') == 'Customer':
# customer LPO
row += [voucher_details.get(gle.voucher_no, {}).get("po_no")]
# Delivery Note
row += [voucher_details.get(gle.voucher_no, {}).get("delivery_note")]
# customer territory / supplier type
if args.get("party_type") == "Customer":
row += [self.get_territory(gle.party), self.get_customer_group(gle.party)]
@ -225,12 +252,13 @@ class ReceivablePayableReport(object):
return self.party_map
def get_voucher_details(self, party_type):
def get_voucher_details(self, party_type, dn_details):
voucher_details = frappe._dict()
if party_type == "Customer":
for si in frappe.db.sql("""select name, due_date
for si in frappe.db.sql("""select name, due_date, po_no
from `tabSales Invoice` where docstatus=1""", as_dict=1):
si['delivery_note'] = dn_details.get(si.name)
voucher_details.setdefault(si.name, si)
if party_type == "Supplier":
@ -363,3 +391,39 @@ def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_dat
outstanding_range[index] = outstanding_amount
return [age] + outstanding_range
def get_pdc_details(party_type):
pdc_details = frappe._dict()
for pdc in frappe.db.sql("""
select
pref.reference_name as invoice_no, pent.party, pent.party_type,
max(pent.reference_date) as pdc_date, sum(ifnull(pref.allocated_amount,0)) as pdc_amount,
GROUP_CONCAT(pent.reference_no SEPARATOR ', ') as pdc_ref
from
`tabPayment Entry` as pent inner join `tabPayment Entry Reference` as pref
on
(pref.parent = pent.name)
where
pent.docstatus = 0 and pent.reference_date > pent.posting_date
and pent.party_type = %s
group by pref.reference_name""", party_type, as_dict=1):
pdc_details.setdefault(pdc.invoice_no, pdc)
return pdc_details
def get_dn_details(party_type):
dn_details = frappe._dict()
if party_type == "Customer":
for si in frappe.db.sql("""select parent, GROUP_CONCAT(delivery_note SEPARATOR ', ') as dn
from `tabSales Invoice Item`
where docstatus=1 and delivery_note is not null and delivery_note != '' group by parent
Union
select against_sales_invoice as parent, GROUP_CONCAT(parent SEPARATOR ', ') as dn
from `tabDelivery Note Item`
where docstatus=1 and against_sales_invoice is not null
and against_sales_invoice != '' group by against_sales_invoice""", as_dict=1):
dn_details.setdefault(si.parent, si.dn)
return dn_details

View File

@ -336,7 +336,7 @@ def get_data():
"color": "#009248",
"icon": "/assets/erpnext/images/hub_logo.svg",
"type": "page",
"link": "hub",
"link": "Hub/Home",
"label": _("Hub")
},
{

View File

@ -415,13 +415,15 @@ class calculate_taxes_and_totals(object):
self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
grand_total = self.doc.rounded_total or self.doc.grand_total
if self.doc.party_account_currency == self.doc.currency:
invoice_total = flt(self.doc.grand_total - flt(self.doc.write_off_amount),
invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
self.doc.precision("grand_total"))
else:
base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
self.doc.precision("base_write_off_amount"))
invoice_total = flt(self.doc.grand_total * self.doc.conversion_rate,
invoice_total = flt(grand_total * self.doc.conversion_rate,
self.doc.precision("grand_total")) - base_write_off_amount
if invoice_total > 0 and self.doc.total_advance > invoice_total:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 498 KiB

View File

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 550 KiB

After

Width:  |  Height:  |  Size: 550 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

Before

Width:  |  Height:  |  Size: 985 KiB

After

Width:  |  Height:  |  Size: 985 KiB

View File

@ -8,8 +8,6 @@ To create a **Course** enter the Course name and Code. Code for the course shoul
Once a **Course** is created, a course schedule can defined for the same.
<img class="screenshot" alt="Course" src="{{docs_base_url}}/assets/img/education/setup/Course.gif">
The Course form is further linked to **Program, Student Group and Assessment Plan** doctypes. The links allow to view/create the related documents for a **Course**.
#### Video Tutorial for Course

View File

@ -3,14 +3,14 @@
The Education Settings page allow you to setup basic settings like **Academic Year and Term** for the educational setup.
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/student/education.png">
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/setup/education-settings.png">
The checkbox to Validate Batch for Students in Student Group enables the Student Batch validation for every Student from the Program Enrollment for the **Batch** based on **Student Group**
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/student/student-batch-validation.gif">
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/setup/student-batch-validation.gif">
You can enable the validation of Course for every Student from the enrolled Courses in Program Enrollment,for Course based Student Group by checking the settings for **Validate Enrolled Course for Students in Student Group**
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/student/student-course-validation.gif">
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/setup/student-course-validation.gif">
{next}

View File

@ -1,8 +1,8 @@
# Setup
The Setup section of education module provides facility to make some basic configuration. Below are doctypes for basic configuration.
The Setup section of education module provides facility to make some basic master records and configuration. Below are doctypes for those masters and configuration.
<img class="screenshot" alt="Setup Section" src="{{docs_base_url}}/assets/img/education/setup/setup-section.png">
<img class="screenshot" alt="Setup Section" src="{{docs_base_url}}/assets/img/education/setup/setup.png">
### Topics

View File

@ -2,11 +2,15 @@
Student Category doctype allow you to classify student based various categories. In Institutions, there may be fee concession for some categories such as Handicapped students, foreign, nationals, reserved category by the government etc.
To create Student category go to Setup >> Student Category >> New.
To create Student category go to:
> Setup > Student Category > New
We can create new student category by adding a name and save it
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/student/student-category.gif">
<img class="screenshot" alt="Student" src="{{docs_base_url}}/assets/img/education/setup/student-category.png">
You can select the Student Category while making the Fee Structure and accordingly the student from the selected groups can be filtered out while making the Fee Schedule.
{next}

View File

@ -15,23 +15,24 @@ def enable_hub():
return hub_settings
@frappe.whitelist()
def get_items(start=0, limit=20, category=None, order_by=None, company=None, text=None):
def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=None):
connection = get_client_connection()
filters = {
'hub_category': category,
}
if text:
filters.update({'item_name': ('like', '%' + text + '%')})
if company:
filters.update({'company_name': company})
filters = json.loads(filters)
response = connection.get_list('Hub Item',
response = connection.get_list(doctype,
limit_start=start, limit_page_length=limit,
filters=filters)
filters=filters, fields=fields)
return response
@frappe.whitelist()
def get_meta(doctype):
connection = get_client_connection()
meta = connection.get_doc('DocType', doctype)
return meta
@frappe.whitelist()
def get_categories():
# get categories info with parent category and stuff
connection = get_client_connection()
response = connection.get_list('Hub Category')
return response
@ -41,7 +42,9 @@ def get_item_details(hub_sync_id=None):
if not hub_sync_id:
return
connection = get_client_connection()
return connection.get_doc('Hub Item', hub_sync_id)
item_details = connection.get_doc('Hub Item', hub_sync_id)
print(item_details)
return item_details
@frappe.whitelist()
def get_company_details(hub_sync_id):

View File

@ -1,8 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Hub Category', {
refresh: function(frm) {
}
});

View File

@ -1,275 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:hub_category_name",
"beta": 0,
"creation": "2017-08-22 11:31:10.410322",
"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": "hub_category_name",
"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": "Category Name",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "parent_hub_category",
"fieldtype": "Link",
"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": "Parent Category",
"length": 0,
"no_copy": 0,
"options": "Hub Category",
"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_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_group",
"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": "Is Group",
"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_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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": 0,
"in_standard_filter": 0,
"label": "Description",
"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_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lft",
"fieldtype": "Int",
"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": "Left",
"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_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rgt",
"fieldtype": "Int",
"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": "Right",
"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_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "old_parent",
"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": "Old Parent",
"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
}
],
"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": "2017-09-03 22:04:22.958831",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Category",
"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": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "hub_category_name",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils.nestedset import NestedSet
from frappe.model.document import Document
class HubCategory(NestedSet):
pass

View File

@ -1,4 +0,0 @@
frappe.treeview_settings["Hub Category"] = {
title: __("Hub Category"),
breadcrumb: "Hub"
}

View File

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

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestHubCategory(unittest.TestCase):
pass

View File

@ -1,873 +0,0 @@
/* globals Hub, HubList */
frappe.provide('erpnext.hub');
frappe.pages['hub'].on_page_load = function(wrapper) {
const page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Hub',
single_col: false
});
wrapper.hub_page = new erpnext.hub.Hub({ page });
};
frappe.pages['hub'].on_page_show = function(wrapper) {
const hub_page = wrapper.hub_page;
const [hub, type, id] = frappe.get_route();
if (!(hub || type || id)) {
hub_page.go_to_home_page();
return;
}
if (type === "Products") {
hub_page.go_to_item_page(id);
} else if (type === "Company") {
hub_page.go_to_company_page(id);
}
}
erpnext.hub.Hub = class Hub {
constructor({ page }) {
this.page = page;
frappe.require('/assets/erpnext/css/hub.css', () => {
this.setup();
});
}
setup() {
this.setup_header();
this.company_cache = {};
this.item_cache = {};
this.filters = {};
this.order_by = '';
this.$hub_main_section =
$(`<div class='hub-main-section'>`).appendTo(this.page.body);
this.bind_events();
this.refresh();
}
refresh() {
this.$hub_main_section.empty();
this.page.page_form.hide();
const $layout_main = this.page.wrapper.find('.layout-main');
const $page_head = this.page.wrapper.find('.page-head');
frappe.model.with_doc('Hub Settings', 'Hub Settings', () => {
this.hub_settings = frappe.get_doc('Hub Settings');
if(this.hub_settings.enabled == 0) {
let $empty_state = this.page.get_empty_state(
__("Register for Hub"),
__(`Let other ERPNext users discover your products
and automate workflow with Supplier from within ERPNext.`),
__("Register")
);
$page_head.hide();
$layout_main
.find('.layout-side-section, .layout-main-section-wrapper')
.hide();
$layout_main.append($empty_state);
$empty_state.find('.btn-primary').on('click', () => {
this.register_for_hub();
});
} else {
$page_head.show();
$layout_main.find('.page-card-container').remove();
$layout_main.find('.layout-side-section, .layout-main-section-wrapper').show();
this.setup_live_state();
}
});
}
register_for_hub() {
if (frappe.session.user.includes('Administrator')) {
frappe.throw(__('Please login as another user.'))
}
frappe.verify_password(() => {
frappe.call({
method: 'erpnext.hub_node.enable_hub',
callback: (r) => {
if(r.message.enabled == 1) {
Object.assign(this.hub_settings, r.message);
this.refresh();
this.prompt_for_item_sync();
}
}
});
});
}
prompt_for_item_sync() {
frappe.call({
method: 'frappe.client.get_list',
args: {
doctype: 'Data Migration Run',
filters: {
'data_migration_plan': 'Hub Sync'
},
limit_page_length: 1
},
callback: function(r) {
if (!r) {
frappe.confirm(__('Do you want to publish your Items to Hub ?'), () => {
this.sync_items_to_hub();
});
}
}
})
}
setup_header() {
this.page.page_title = this.page.wrapper.find('.page-title');
this.tag_line = $(`
<div class='tag-line-container'>
<span class='tag-line text-muted small'>
${__('Product listing and discovery for ERPNext users')}
</span>
</div>`)
.appendTo(this.page.page_title);
this.bind_title();
}
setup_live_state() {
if(!this.$search) {
this.setup_filters();
}
this.page.page_form.show();
this.setup_menu();
this.setup_sidebar();
this.render_body();
this.setup_lists();
}
setup_filters() {
// frappe.call({
// method: 'erpnext.hub_node.get_categories'
// }).then((r) => {
// if (r.message) {
// const categories = r.message;
// console.log("categories", categories);
// categories
// .map(c => c.hub_category_name)
// .map(c => this.sidebar.add_item({
// label: c,
// on_click: () => {
// this.home_item_list &&
// this.home_item_list.refresh({
// text: '',
// start: 0,
// limit: 20,
// category: c && c !== 'All Categories' ? c : undefined
// });
// }
// }, __('Hub Category')));
// }
// });
// this.category_select = this.page.add_select(__('Category'),
// [
// {label: __('Sort by Price ...'), value: '' },
// {label: __('High to Low'), value: 'price desc' },
// {label: __('Low to High'), value: 'price' },
// ]
// );
this.price_sort_select = this.page.add_select(__('Sort by Price'),
[
{label: __('Sort by Price ...'), value: '' },
{label: __('High to Low'), value: 'price desc' },
{label: __('Low to High'), value: 'price' },
]
);
this.criteria_select = this.page.add_select(__('Sort by Criteria'),
[
{label: __('Most Popular'), value: 'request_count' },
{label: __('Newest'), value: 'creation' },
]
);
this.price_sort_select.on('change', () => {
this.refresh_item_only_page();
});
this.criteria_select.on('change', () => {
this.refresh_item_only_page();
});
this.setup_hub_category_filter();
this.setup_search();
}
bind_events() {
const me = this;
this.$hub_main_section
.on('click', '.company-link a', function(e) {
e.preventDefault();
const company_name = $(this).attr('data-company-name');
frappe.set_route('hub', 'Company', company_name);
})
.on('click', '.breadcrumb li', function(e) {
e.preventDefault();
const $li = $(this);
if ($li.attr('data-route') === 'Home') {
me.go_to_home_page();
}
});
}
update_filters() {
let price_sort = $(this.price_sort_select).val() || '';
let criteria = $(this.criteria_select).val() || '';
let order_by_params = [];
let query_string = '';
if(criteria) {
order_by_params.push(criteria);
// query_string += 'sort_by=' + criteria
}
if(price_sort) order_by_params.push(price_sort);
this.order_by = order_by_params.join(",");
// return query_string;
}
reset_filters() {
this.order_by = '';
$(this.category_select).val('');
$(this.price_sort_select).val('');
$(this.criteria_select).val('Most Popular');
}
refresh_item_only_page() {
this.reset_search();
this.update_filters();
this.go_to_items_only_page(
['hub', 'Products'],
'', 'product-list'
);
}
bind_title() {
this.page.page_title.find('.title-text').on('click', () => {
this.go_to_home_page();
});
}
render_body() {
this.$home_page = $(`
<div class = 'hub-home-page'>
<div class='banner'></div>
<div class='listing-body row'>
<div class='main-list-section'></div>
</div>
</div>
`).appendTo(this.$hub_main_section);
this.$banner = this.$hub_main_section.find('.banner');
this.$listing_body = this.$hub_main_section.find('.listing-body');
this.$main_list_section = this.$hub_main_section.find('.main-list-section');
this.$side_list_section = this.$hub_main_section.find('.side-list-section');
}
setup_lists() {
this.home_item_list = new erpnext.hub.HubList({
parent: this.$main_list_section,
title: 'New',
page_length: 20,
list_css_class: 'home-product-list',
method: 'erpnext.hub_node.get_items',
// order_by: 'request_count',
filters: {text: '', country: this.country}, // filters at the time of creation
on_item_click: (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
});
this.home_item_list.setup();
}
setup_hub_category_filter() {
const me = this;
this.hub_category_field = this.page.add_field({
fieldtype: 'Autocomplete',
label: 'Hub Category',
change() {
let value = this.get_value();
let title = value;
if (value === 'All Categories') {
// show all items
value = null;
}
me.home_item_list.title = title;
me.home_item_list.refresh({
text: '',
start: 0,
limit: 20,
category: value
});
}
});
frappe.call('erpnext.hub_node.get_categories')
.then((r) => {
if (r.message) {
const categories = r.message;
this.hub_category_field.set_data(
categories.map(c => c.hub_category_name)
);
}
});
}
setup_search() {
this.$search = this.page.add_data(__('Search'));
this.$search.on('keypress', (e) => {
if(e.which === 13) {
var search_term = ($(this.$search).val() || '').toLowerCase();
this.go_to_items_only_page(
['hub', 'search', search_term],
'Search results for \'' + search_term + '\'',
'search-product-list',
{text: search_term}
);
}
});
}
go_to_items_only_page(route, title, class_name, filters = {text: ''}, by_item_codes=0) {
frappe.set_route(route);
this.$hub_main_section.empty();
this.filtered_item_list = new erpnext.hub.HubList({
parent: this.$hub_main_section,
title: title,
page_length: 20,
list_css_class: class_name,
method: 'erpnext.hub_node.get_items',
order_by: this.order_by,
filters: filters,
by_item_codes: by_item_codes
});
this.filtered_item_list.on_item_click = (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
this.filtered_item_list.setup();
}
go_to_item_page(item_code) {
if(this.item_cache) {
let item = this.item_cache[item_code];
if(item) {
this.render_item_page(item);
return;
}
} else {
this.item_cache = {};
}
frappe.call({
args:{
hub_sync_id: item_code
},
method: "erpnext.hub_node.get_item_details",
callback: (r) => {
if (!r || !r.message) return;
let item = r.message;
this.item_cache[item_code] = item;
this.render_item_page(item);
}
});
}
render_item_page(item) {
this.$hub_main_section.empty();
let $item_page =
$(this.get_item_page(item))
.appendTo(this.$hub_main_section);
let $company_items = $item_page.find('.company-items');
let company_item_list = new erpnext.hub.HubList({
parent: $company_items,
title: 'More by ' + item.company_name,
page_length: 5,
list_css_class: 'company-item-list',
method: 'erpnext.hub_node.get_items',
// order_by: 'request_count',
filters: {text: '', company_name: item.company_name, country: this.country},
paginated: 0,
img_size: 150
});
company_item_list.on_item_click = (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
company_item_list.setup();
$item_page.find('.rfq-btn')
.click((e) => {
const $btn = $(e.target);
this.show_rfq_modal(item)
.then(values => {
item.item_code = values.item_code;
delete values.item_code;
const supplier = values;
return [item, supplier];
})
.then(([item, supplier]) => {
return this.make_rfq(item, supplier, $btn);
})
.then(r => {
console.log(r);
if (r.message && r.message.rfq) {
$btn.addClass('disabled').html(`<span><i class='fa fa-check'></i> ${__('Quote Requested')}</span>`);
} else {
throw r;
}
})
.catch((e) => {
console.log(e); //eslint-disable-line
});
});
}
show_rfq_modal(item) {
return new Promise(res => {
let fields = [
{ label: __('Item Code'), fieldtype: 'Data', fieldname: 'item_code', default: item.item_code },
{ fieldtype: 'Column Break' },
{ label: __('Item Group'), fieldtype: 'Link', fieldname: 'item_group', default: item.item_group },
{ label: __('Supplier Details'), fieldtype: 'Section Break' },
{ label: __('Supplier Name'), fieldtype: 'Data', fieldname: 'supplier_name', default: item.company_name },
{ label: __('Supplier Email'), fieldtype: 'Data', fieldname: 'supplier_email', default: item.seller },
{ fieldtype: 'Column Break' },
{ label: __('Supplier Type'), fieldname: 'supplier_type',
fieldtype: 'Link', options: 'Supplier Type' }
];
fields = fields.map(f => { f.reqd = 1; return f; });
const d = new frappe.ui.Dialog({
title: __('Request for Quotation'),
fields: fields,
primary_action_label: __('Send'),
primary_action: (values) => {
res(values);
d.hide();
}
});
d.show();
});
}
get_company_details(company_id) {
this.company_cache = this.company_cache || {};
return new Promise(resolve => {
// get from cache if exists
let company_details = this.company_cache[company_id];
if(company_details) {
resolve(company_details);
return;
}
frappe.call({
method: 'erpnext.hub_node.get_company_details',
args: {hub_sync_id: company_id}
}).then((r) => {
if (r.message) {
const company_details = r.message;
this.company_cache[company_id] = company_details;
resolve(company_details)
}
});
})
}
go_to_company_page(company_id) {
this.get_company_details(company_id)
.then(this.show_company_page.bind(this));
}
show_company_page(company_details) {
this.$hub_main_section.empty();
let $company_page =
$(this.get_company_page(company_details))
.appendTo(this.$hub_main_section);
let $company_items = $company_page.find('.company-items');
let company_item_list = new erpnext.hub.HubList({
parent: $company_items,
title: 'More by ' + company_details.company_name,
page_length: 5,
list_css_class: 'company-item-list',
method: 'erpnext.hub_node.get_items',
// order_by: 'request_count',
filters: {text: '', company: company_details.company_name, country: this.country},
paginated: 0,
img_size: 150
});
company_item_list.on_item_click = (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
company_item_list.setup();
}
get_item_page(item) {
return `
<div class="hub-item-page">
<div class="item-header">
<div class="item-page-image">
${ this.home_item_list.get_item_image(item) }
</div>
<div class="title-content">
<div class="breadcrumbs">
${this.get_breadcrumb(item.item_name, "Products") }
</div>
<div class="title">
<h2>${ item.item_name }</h2>
</div>
<div class="company">
<span class="">${ item.company_name }</span>
</div>
<div class="category">
<span class="text-muted">Products</span>
</div>
<div class="description">
<span class="small">${ item.description ? item.description : "" }</span>
</div>
<div class="price">
${ item.formatted_price ? item.formatted_price : '' }
</div>
<div class="actions">
<a class="btn btn-primary rfq-btn">Request A Quote</a>
</div>
</div>
</div>
<div class="item-more-info"></div>
<div class="company-items">
</div>
</div>
`;
}
get_company_page(company_details) {
return `
<div class="hub-item-page">
<div class="item-header">
<div class="title-content">
<div class="breadcrumbs">
${this.get_breadcrumb(company_details.company_name, "Company") }
</div>
<div class="title">
<h2>${ company_details.company_name }</h2>
</div>
<div class="company">
<span class="">${ company_details.country }</span>
</div>
<div class="description">
<span class="small">${ company_details.site_name }</span>
</div>
</div>
</div>
<div class="item-more-info"></div>
<div class="company-items">
</div>
</div>
`;
}
get_breadcrumb(name, type) {
return `
<ul class="breadcrumb">
<li data-route="Home">
<a href><span>Home</span></a>
</li>
<li data-route="List">
<a href><span>${type}</span></a>
</li>
<li class="active">
<span>${name}</span>
</li>
</ul>
`;
}
go_to_home_page() {
frappe.set_route('hub');
this.reset_filters();
this.refresh();
}
setup_menu() {
if (this.menu_setup) return;
this.page.add_menu_item(__('Hub Settings'),
() => frappe.set_route('Form', 'Hub Settings'));
this.page.add_menu_item(__('Refresh'), () => this.refresh());
this.page.add_menu_item(__('Sync'), () => this.sync_items_to_hub());
this.menu_setup = true;
}
sync_items_to_hub() {
frappe.call('erpnext.hub_node.doctype.hub_settings.hub_settings.sync')
}
setup_sidebar() {
var me = this;
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
this.add_account_to_sidebar();
}
add_account_to_sidebar() {
this.sidebar.add_item({
label: this.hub_settings.company,
on_click: () => frappe.set_route('Form', 'Company', this.hub_settings.company)
}, __("Account"));
this.sidebar.add_item({
label: __("My Orders"),
on_click: () => frappe.set_route('List', 'Request for Quotation')
}, __("Account"));
}
get_search_term() {
return this.$search.val();
}
reset_search() {
this.$search.val('');
}
make_rfq(item, supplier, btn) {
console.log(supplier);
return new Promise((resolve, reject) => {
frappe.call({
method: 'erpnext.hub_node.make_rfq_and_send_opportunity',
args: { item, supplier },
callback: resolve,
btn,
}).fail(reject);
});
}
go_to_seen_items() {
this.go_to_items_only_page(
['hub', 'Requested Products'],
__('Requested Products'),
'requested-product-list',
{}, 1
);
}
}
erpnext.hub.HubList = class HubList {
constructor({
parent = null,
title = 'Products',
page_length = 20,
list_css_class = '',
method = 'erpnext.hub_node.get_items',
filters = {text: ''},
order_by = '',
by_item_codes = 0,
paginated = 1,
on_item_click = null,
img_size = 200
}) {
this.parent = parent;
this.title = title;
this.page_length = page_length;
this.list_css_class = list_css_class;
this.method = method;
this.filters = filters;
this.order_by = order_by;
this.by_item_codes = by_item_codes;
this.paginated = paginated;
this.on_item_click = on_item_click;
this.img_size = img_size;
}
// to be called on demand
setup() {
this.container = $(`
<div class='item-list-container ${this.list_css_class}' data-page-length='${this.page_length}'>
<div class='item-list-header'>
<h3>${this.title}</h3>
</div>
<div class='item-list'></div>
<div class='list-state'>
<div class='loading'>
<p class='text-muted text-center'>${__('Loading...')}</p>
</div>
<div class='done hide'>
<p class='text-muted text-center'>${__('No more results')}</p>
</div>
<div class='more text-right'>
<button class='btn btn-default btn-sm'>${__('More')}</div>
</div>
</div>
</div>`)
.appendTo(this.parent);
this.$item_list_title = this.container.find('.item-list-header h3');
this.$list = this.container.find('.item-list');
this.$loading = this.container.find('.loading').hide();
this.$more = this.container.find('.more').hide();
this.$done = this.container.find('.done');
this.$more.on('click', () => {
this.next_page();
});
this.next_page();
}
refresh(filters = this.filters) {
this.reset();
this.set_filters(filters);
this.next_page();
}
reset() {
this.$list.empty();
}
set_filters(filters) {
this.filters = filters;
}
next_page() {
this.$item_list_title.html(this.title);
const start = this.$list.find('.hub-item-wrapper').length;
this.$loading.show();
// build args
let args = {
start: start,
// query one extra
limit: this.page_length + 1
};
Object.assign(args, this.filters);
console.log("filters: ", args);
args.order_by = this.order_by;
args.by_item_codes = this.by_item_codes;
frappe.call({
method: this.method,
args: args,
callback: (r) => {
let items = r.message;
console.log("items: ", items);
this.render_items(items);
}
});
}
render_items(items) {
if(items) {
// clear any filler divs
this.$list.find('.filler').remove();
let done = 0;
console.log("items length", items.length);
if(items.length && items.length > this.page_length) {
// remove the extra queried
items.pop();
} else {
done = 1;
}
items.forEach((item) => {
this.make_item_card(item).appendTo(this.$list);
});
const remainder = items.length % 4;
if (remainder > 0) {
// fill with filler divs to make flexbox happy
Array.from(Array(remainder))
.map(r => $('<div class="filler">').css('width', '200px').appendTo(this.$list));
}
this.update_list_state(done);
} else {
this.update_list_state(1);
}
}
update_list_state(done=0) {
this.$loading.hide();
if(done) {
this.$done.removeClass('hide');
this.$more.hide();
} else {
this.$more.show();
this.$done.addClass('hide');
}
}
make_item_card(item) {
let $item_card = $(`
<div class="hub-item-wrapper" style="max-width: ${this.img_size}px;">
<a class="item-link" href>
<div class="hub-item-image">
${ this.get_item_image(item) }
</div>
<div class="hub-item-title">
<h5 class="bold">
${!item.seen ? item.item_name : `<span class="indicator blue">${item.item_name}</span>`}
<h5>
</div>
</a>
<div class="company-link">
<a data-company-name="${ item.company_name }" class="">${ item.company_name }</a>
</div>
<div>${ item.formatted_price ? item.formatted_price : ''}</div>
</div>
`);
$item_card.find(".item-link").click((e) => {
e.preventDefault();
this.on_item_click && this.on_item_click(item.name);
});
return $item_card;
}
get_item_image(item, size=this.img_size) {
const _size = size + 'px';
const item_image = item.image ?
`<img src="${item.image}"><span class="helper"></span>` :
`<div class="standard-image">${item.item_name[0]}</div>`;
return `
<div class="img-wrapper"
style="max-width: ${_size}; width: ${_size}; height: ${_size};">
${item_image}
</div>`;
}
}

View File

@ -1,21 +0,0 @@
{
"content": null,
"creation": "2015-02-18 05:17:17.301735",
"docstatus": 0,
"doctype": "Page",
"modified": "2015-02-18 05:17:17.301735",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "hub",
"owner": "Administrator",
"page_name": "hub",
"roles": [
{
"role": "All"
}
],
"script": null,
"standard": "Yes",
"style": null,
"title": "Hub"
}

View File

@ -36,7 +36,8 @@
"public/js/utils/item_quick_entry.js",
"public/js/utils/customer_quick_entry.js",
"public/js/education/student_button.html",
"public/js/education/assessment_result_tool.html"
"public/js/education/assessment_result_tool.html",
"public/js/hub/hub_factory.js"
],
"js/item-dashboard.min.js": [
"stock/dashboard/item_dashboard.html",

View File

@ -1,3 +1,29 @@
body[data-route^="Hub/"] .freeze .image-view-container .list-row-col {
background-color: #fafbfc;
color: #fafbfc;
}
body[data-route^="Hub/"] .freeze .image-view-container .placeholder-text {
color: #fafbfc;
}
body[data-route^="Hub/"] .freeze {
display: none;
}
body[data-route^="Hub/"] .image-view-container {
justify-content: space-around;
}
.img-wrapper {
border: 1px solid #d1d8dd;
border-radius: 3px;
padding: 12px;
overflow: hidden;
text-align: center;
white-space: nowrap;
}
.img-wrapper .helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
/* hub */
div[data-page-route="hub"] .page-head {
height: 80px;

View File

@ -458,7 +458,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
var party = me.frm.doc[frappe.model.scrub(party_type)];
if(party) {
if(party && me.frm.doc.company) {
return frappe.call({
method: "erpnext.accounts.party.get_party_account",
args: {

View File

@ -0,0 +1,51 @@
frappe.provide('erpnext.hub.pages');
frappe.views.HubFactory = frappe.views.Factory.extend({
make(route) {
const page_name = frappe.get_route_str();
const page = route[1];
const assets = {
'List': [
'/assets/erpnext/js/hub/hub_page.js',
'/assets/erpnext/css/hub.css',
],
'Form': [
'/assets/erpnext/js/hub/hub_form.js',
'/assets/erpnext/css/hub.css',
]
};
if (!erpnext.hub.pages[page_name]) {
if (page === 'Item' && !route[2]) {
frappe.require(assets['List'], () => {
erpnext.hub.pages[page_name] = new erpnext.hub.ItemListing({
doctype: 'Hub Settings',
parent: this.make_page(true, page_name)
});
window.hub_page = erpnext.hub.pages[page_name];
});
} if (page === 'Company' && !route[2]) {
frappe.require(assets['List'], () => {
erpnext.hub.pages[page_name] = new erpnext.hub.CompanyListing({
doctype: 'Hub Settings',
parent: this.make_page(true, page_name)
});
window.hub_page = erpnext.hub.pages[page_name];
});
} else if(route[2]) {
frappe.require(assets['Form'], () => {
erpnext.hub.pages[page_name] = new erpnext.hub.HubForm({
hub_item_code: route[2],
doctype: 'Hub Settings',
parent: this.make_page(true, page_name)
});
window.hub_page = erpnext.hub.pages[page_name];
});
}
} else {
frappe.container.change_to(page_name);
window.hub_page = erpnext.hub.pages[page_name];
}
}
});

View File

@ -0,0 +1,84 @@
frappe.provide('erpnext.hub');
erpnext.hub.HubForm = class HubForm extends frappe.views.BaseList {
setup_defaults() {
super.setup_defaults();
this.page_title = this.data.item_name || this.hub_item_code || __('Hub Item');
this.method = 'erpnext.hub_node.get_item_details';
}
setup_fields() {
this.fields = ['hub_item_code', 'item_name', 'item_code', 'description', 'seller', 'company_name', 'country'];
}
set_breadcrumbs() {
frappe.breadcrumbs.add({
label: __('Hub'),
route: '#Hub/Item',
type: 'Custom'
});
}
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.$page.find('.layout-side-section'),
css_class: 'hub-form-sidebar'
});
}
setup_filter_area() {
}
setup_sort_selector() {
}
get_args() {
return {
hub_sync_id: this.hub_item_code
};
}
prepare_data(r) {
this.data = r.message;
}
update_data(r) {
this.data = r.message;
}
render() {
this.sidebar.add_item({
label: `<img src="${this.data.image}" />`
});
let fields = [];
this.fields.map(fieldname => {
fields.push({
label: toTitle(frappe.model.unscrub(fieldname)),
fieldname,
fieldtype: 'Data',
read_only: 1
});
});
this.form = new frappe.ui.FieldGroup({
parent: this.$result,
fields
});
this.form.make();
this.form.set_values(this.data);
}
toggle_result_area() {
this.$result.toggle(this.data.hub_item_code);
this.$paging_area.toggle(this.data.length > 0);
this.$no_result.toggle(this.data.length == 0);
const show_more = (this.start + this.page_length) <= this.data.length;
this.$paging_area.find('.btn-more')
.toggle(show_more);
}
};

View File

@ -0,0 +1,221 @@
frappe.provide('erpnext.hub');
erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList {
setup_defaults() {
super.setup_defaults();
this.page_title = __('Hub');
this.method = 'erpnext.hub_node.get_list';
const route = frappe.get_route();
this.page_name = route[1];
}
setup_fields() {
return this.get_meta()
.then(r => {
console.log('fields then', this.doctype);
this.meta = r.message || this.meta;
frappe.model.sync(this.meta);
});
}
get_meta() {
console.log('get_meta', this.doctype);
return new Promise(resolve =>
frappe.call('erpnext.hub_node.get_meta', {doctype: this.doctype}, resolve));
}
set_breadcrumbs() { }
setup_side_bar() { }
setup_sort_selector() { }
setup_view() { }
get_args() {
return {
doctype: this.doctype,
start: this.start,
limit: this.page_length,
order_by: this.order_by,
fields: this.fields,
filters: this.get_filters_for_args()
};
}
update_data(r) {
const data = r.message;
console.log('update data', data);
if (this.start === 0) {
this.data = data;
} else {
this.data = this.data.concat(data);
}
}
freeze(toggle) {
this.$freeze.toggle(toggle);
if (this.$freeze.find('.image-view-container').length) return;
const html = Array.from(new Array(4)).map(d => this.card_html({
name: 'Loading...',
item_name: 'Loading...'
})).join('');
this.$freeze.html(`<div class="image-view-container border-top">${html}</div>`);
}
render() {
this.render_image_view();
}
render_image_view() {
let data = this.data;
// console.log('this.data render', this.data);
if (this.start === 0) {
this.$result.html('<div class="image-view-container small padding-top">');
data = this.data.slice(this.start);
}
var html = data.map(this.card_html.bind(this)).join("");
this.$result.find('.image-view-container').append(html);
}
}
erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing {
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Item';
this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name'];
this.filters = [];
this.custom_filter_configs = [
{
fieldtype: 'Data',
label: 'Company',
condition: 'like',
fieldname: 'company_name',
},
{
fieldtype: 'Link',
label: 'Country',
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
}
get_filters_for_args() {
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'item_name';
filters[field] = [f[2], f[3]];
});
if(this.current_category) {
filters['hub_category'] = this.current_category;
}
return filters;
}
card_html(item) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item['item_name' || 'item_code']);
// console.log(item);
const company_name = item['company_name'];
const route = `#Hub/Item/${item.hub_item_code}`;
const image_html = item.image ?
`<img src="${item.image}">
<span class="helper"></span>` :
`<div class="standard-image">${frappe.get_abbr(title)}</div>`;
return `
<div class="hub-item-wrapper margin-bottom" style="width: 200px;">
<a href="${route}">
<div class="hub-item-image">
<div class="img-wrapper" style="height: 200px; width: 200px">
${image_html}
</div>
</div>
<div class="hub-item-title">
<h5 class="bold">
${ title }
</h5>
<p>${ company_name }</p>
</div>
</a>
</div>
`;
}
};
erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing {
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Company';
this.fields = ['name', 'site_name', 'seller_city', 'seller_description', 'seller', 'country', 'company_name'];
this.filters = [];
this.custom_filter_configs = [
{
fieldtype: 'Data',
label: 'Company',
condition: 'like',
fieldname: 'company_name',
},
{
fieldtype: 'Link',
label: 'Country',
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
}
get_filters_for_args() {
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'company_name';
filters[field] = [f[2], f[3]];
});
return filters;
}
card_html(item) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item['item_name' || 'item_code']);
// console.log(item);
const company_name = item['company_name'];
const route = `#Hub/Item/${item.hub_item_code}`;
const image_html = item.image ?
`<img src="${item.image}">
<span class="helper"></span>` :
`<div class="standard-image">${frappe.get_abbr(title)}</div>`;
return `
<div class="hub-item-wrapper margin-bottom" style="width: 200px;">
<a href="${route}">
<div class="hub-item-image">
<div class="img-wrapper" style="height: 200px; width: 200px">
${image_html}
</div>
</div>
<div class="hub-item-title">
<h5 class="bold">
${ title }
</h5>
<p>${ company_name }</p>
</div>
</a>
</div>
`;
}
};

View File

@ -1,5 +1,41 @@
@import "../../../../frappe/frappe/public/less/variables.less";
body[data-route^="Hub/"] {
.freeze .image-view-container {
.list-row-col {
background-color: @light-bg;
color: @light-bg;
}
.placeholder-text {
color: @light-bg;
}
}
.freeze {
display: none;
}
.image-view-container {
justify-content: space-around;
}
}
.img-wrapper {
border: 1px solid #d1d8dd;
border-radius: 3px;
padding: 12px;
overflow: hidden;
text-align: center;
white-space: nowrap;
.helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
}
/* hub */
div[data-page-route="hub"] {
.page-head {

View File

@ -3529,8 +3529,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-19 14:51:52.710612",
"modified_by": "nabinhait@gmail.com",
"modified": "2018-01-12 15:56:12.483019",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
"owner": "Administrator",

View File

@ -476,13 +476,10 @@ def make_project(source_name, target_doc=None):
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target):
if source.po_no:
if target.po_no:
target_po_no = target.po_no.split(", ")
target_po_no.append(source.po_no)
target.po_no = ", ".join(list(set(target_po_no))) if len(target_po_no) > 1 else target_po_no[0]
else:
target.po_no = source.po_no
so = [d.against_sales_order for d in target.items]
if so:
po_no_list = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', so)})
target.po_no = ', '.join(d.po_no for d in po_no_list if d.po_no)
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")

View File

@ -780,11 +780,19 @@ class POSCart {
return;
}
const item_code = this.selected_item.attr('data-item-code');
const field = this.selected_item.active_field;
const value = this.numpad.get_value();
if (this.selected_item.active_field == 'discount_percentage' && this.numpad.get_value() > cint(100)) {
frappe.show_alert({
indicator: 'red',
message: __('Discount amount cannot be greater than 100%')
});
this.numpad.reset_value();
} else {
const item_code = this.selected_item.attr('data-item-code');
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);
}
}
this.events.on_numpad(btn_value);
@ -1600,4 +1608,4 @@ class Payment {
this.dialog.set_value("paid_amount", this.frm.doc.paid_amount);
this.dialog.set_value("outstanding_amount", this.frm.doc.outstanding_amount);
}
}
}

View File

@ -398,73 +398,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "po_no",
"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": "Customer's Purchase Order No",
"length": 0,
"no_copy": 0,
"oldfieldname": "po_no",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.po_no",
"fieldname": "po_date",
"fieldtype": "Date",
"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": "Customer's Purchase Order Date",
"length": 0,
"no_copy": 0,
"oldfieldname": "po_date",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -527,6 +460,133 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "po_no",
"columns": 0,
"fieldname": "customer_po_details",
"fieldtype": "Section 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,
"label": "Customer PO Details",
"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_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "po_no",
"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": "Customer's Purchase Order No",
"length": 0,
"no_copy": 0,
"oldfieldname": "po_no",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_17",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.po_no",
"fieldname": "po_date",
"fieldtype": "Date",
"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": "Customer's Purchase Order Date",
"length": 0,
"no_copy": 0,
"oldfieldname": "po_date",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -3730,7 +3790,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-01-11 14:40:21.152015",
"modified": "2018-01-12 15:27:44.471335",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

File diff suppressed because it is too large Load Diff