Compare commits

..

108 Commits

Author SHA1 Message Date
Ameya Shenoy
bbd8b04012 Merge branch 'hotfix' 2018-10-17 09:04:11 +00:00
Ameya Shenoy
bb1b6b42e2 bumped to version 10.1.60 2018-10-17 09:04:11 +00:00
Shreya Shah
8e71074e1c fix(report): Add column for Item Name (#15702) 2018-10-16 14:36:49 +05:30
Shreya Shah
ff0deedca9 fix(discount-amount): Print hide discount_amount if print without amount (#15704) 2018-10-16 14:34:30 +05:30
deepeshgarg007
4bb90add1d Currency symbol bug fix (#15698) 2018-10-15 19:07:49 +05:30
Nabin Hait
44ec05f79b fix(bom): deadlock issue via bom replace tool (#15694) 2018-10-15 18:56:16 +05:30
Bibin
7844b79274 [Bug-Fix] accounts_receivable.html (#15688)
* Update accounts_receivable.html

In the print format and PDF the total was showing as 0 (Zero) , when I made these changes it fixed the issue

* Update accounts_receivable.html
2018-10-15 18:25:26 +05:30
Nabin Hait
ea75295bb3 Merge branch 'hotfix' 2018-10-15 15:41:25 +05:30
Nabin Hait
d2c643eb0b bumped to version 10.1.59 2018-10-15 16:11:25 +06:00
rohitwaghchaure
94fcb0e9f9 [Enhance] Add user image in the employee from the user (#15680) 2018-10-15 14:57:46 +05:30
rohitwaghchaure
3362d6b948 [Fix] Precision issue in the expense claim (#15678) 2018-10-15 11:07:13 +05:30
Saurabh
75c1682e61 Merge branch 'hotfix' 2018-10-09 17:55:32 +05:30
Saurabh
2dc8972794 bumped to version 10.1.58 2018-10-09 18:25:32 +06:00
Saurabh
f786eccdf9 Merge pull request #15633 from Zlash65/setup-fix
[Minor] Setup Wizard failing fix
2018-10-09 17:49:54 +05:30
Zlash65
a1036ad50b setup wizard failing fix 2018-10-09 17:38:28 +05:30
Shreya Shah
5b34d00bc0 fix(accounts-receivable): Column values in Print and PDF (#15622) 2018-10-08 18:20:51 +05:30
Ameya Shenoy
ee3b788024 Merge branch 'hotfix' 2018-10-08 09:37:37 +00:00
Ameya Shenoy
b509b06edf bumped to version 10.1.57 2018-10-08 09:37:37 +00:00
Zarrar
5fcccda883 [Minor] Supplier Quotation (#15578)
* throw meaningful error if RFQ does not have selected supplier

* Update supplier_quotation.py

* codacy fix
2018-10-08 14:30:53 +05:30
Shreya Shah
4fb9230d16 Add missing method link_to_mrs to buying.js (#15613) 2018-10-08 14:18:03 +05:30
Shreya Shah
37d3686372 fix(setup_taxes): Pop if frappe.message_log (#15615) 2018-10-08 14:17:16 +05:30
Ameya Shenoy
5eafa5a487 Merge branch 'hotfix' 2018-10-05 08:54:46 +00:00
Ameya Shenoy
97dbb4d125 bumped to version 10.1.56 2018-10-05 08:54:46 +00:00
Shreya Shah
17ac38ff29 Enable save before saving to update form after save (#15579) 2018-10-05 11:51:13 +05:30
Ameya Shenoy
65652071ff Merge branch 'hotfix' 2018-10-04 09:11:50 +00:00
Ameya Shenoy
79dc8ac9cc bumped to version 10.1.55 2018-10-04 09:11:50 +00:00
rohitwaghchaure
15e7646edd Merge pull request #15569 from rohitwaghchaure/stock_adjustment_to_cost_of_goods_sold
Book cost of goods sold instead of stock adjustment
2018-10-03 17:36:29 +05:30
Rohit Waghchaure
d1b87ba41c Book cost of goods sold instead of stock adjustment 2018-10-03 16:29:43 +05:30
rohitwaghchaure
a5576f5b21 [Fix] Stock difference between gl entry and stock ledger entry booked in stock adjustment (#15374) 2018-10-03 10:39:50 +05:30
rohitwaghchaure
8976ad5ca1 Merge pull request #15523 from rohitwaghchaure/fix_attendance_tool_issue
[Fix] Attendance tool
2018-10-01 15:07:49 +05:30
Rohit Waghchaure
c7f8b82fff [Fix] Attendance tool 2018-10-01 12:30:58 +05:30
rohitwaghchaure
6b62b86bbf Merge pull request #15545 from rohitwaghchaure/salary_slip_not_creating_because_of_the_date_issue
[Fix] Salary slip
2018-10-01 12:03:26 +05:30
Rohit Waghchaure
8fbf856618 [Fix] Salary slip 2018-10-01 12:00:45 +05:30
Nabin Hait
7d6d678e8d purchase receipt return entry in dashboard 2018-09-26 19:04:11 +05:30
Nabin Hait
c22ba2ec26 fix(sales return): validation message fix 2018-09-26 18:56:45 +05:30
rohitwaghchaure
0cf0ebf08b [Refactored] Asset Depreciation Ledger report based on GL entries (#15415)
* [Refactored] Asset Depreciation Ledger report is based on GL entries

* Provision to make manual JV from the asset if Calculate Depreciation is disabled
2018-09-26 15:24:49 +05:30
Ameya Shenoy
0ff35a852a Merge branch 'hotfix' 2018-09-26 07:26:49 +00:00
Ameya Shenoy
e04431ea5c bumped to version 10.1.54 2018-09-26 07:26:49 +00:00
rohitwaghchaure
fafc277666 [Fix] BOM update tool, too many writes in one request. Please send smaller requests (#15432) 2018-09-25 18:59:20 +05:30
rohitwaghchaure
fe1e4a41e6 Validate negative stock serial number (#15492) 2018-09-25 18:36:32 +05:30
Faris Ansari
7a8c5b0c2c fix(setup wizard): Validate FY dates (#15473) 2018-09-25 18:34:33 +05:30
Saurabh
056ecdca6a Merge branch 'hotfix' 2018-09-20 13:22:36 +05:30
Saurabh
3b281a0a1b bumped to version 10.1.53 2018-09-20 13:52:36 +06:00
rohitwaghchaure
a60ab1af75 [Fix] Salary slip is not saving (#15449) 2018-09-20 13:18:49 +05:30
rohitwaghchaure
550fc695f1 [Fix] Precision issue in the accounts receivable report (#15440) 2018-09-19 19:03:40 +05:30
Zarrar
013493de7a lable for discount_amount should be configurable (#15408) 2018-09-17 11:06:49 +05:30
Saurabh
b3737c812a Merge branch 'hotfix' 2018-09-12 14:44:25 +05:30
Saurabh
6d5a29af6a bumped to version 10.1.52 2018-09-12 15:14:25 +06:00
Ahmed Madi
20d8c52d2a Update salary_slip.js (#15200) 2018-09-12 11:02:57 +05:30
Nabin Hait
e11f299b21 Update production_order.py 2018-09-11 11:36:05 +05:30
Zarrar
68000c66fc check if Data Migration Plan exists before deleting (#15368) 2018-09-09 19:19:13 +05:30
rohitwaghchaure
3cc77b52e7 [Fix] Timeout error for bom update tool (#15343) 2018-09-07 16:16:58 +05:30
rohitwaghchaure
bd4348db10 [Enhance] Add additional cost of the manufacture stock entry in the project costing (#15341) 2018-09-07 16:16:23 +05:30
Nabin Hait
4fd1cebf95 fix(patch): Delete all documents from Hub Node module 2018-09-06 18:53:46 +05:30
Faris Ansari
b892cc8b02 fix(item): Remove Hub Category field (#15331) 2018-09-06 18:02:48 +05:30
Nabin Hait
8652bce9e3 Update modules.txt 2018-09-06 14:18:49 +05:30
Faris Ansari
4e372a30fa fix(patch): Remove old Hub Page (#15328) 2018-09-06 14:08:31 +05:30
Prateeksha Singh
df327f2974 [remove-hub] delete hub module 2018-09-06 13:34:47 +05:30
Prateeksha Singh
3ccbb8f8b7 [remove-hub] delete hub_settings and hub_category 2018-09-06 13:28:47 +05:30
Zarrar
0d931e1d7d fix uom & stock_uom print hide logic (#15327) 2018-09-06 13:08:58 +05:30
johnhkelley1
431b4fba72 Allow for 0 valuation rate in Stock Reconciliation (#15313)
Currently, a valuation rate of 0 gets treated the same as an omitted
valuation rate, and gets overwritten during validation. This hotfix
allows a Stock Reconciliation Item's valuation rate to be set to 0.
2018-09-05 11:43:28 +05:30
Saurabh
fba985d49d Merge branch 'hotfix' 2018-09-03 17:00:15 +05:30
Saurabh
4bd562d669 bumped to version 10.1.51 2018-09-03 17:30:15 +06:00
Shreya Shah
bb88e964a4 Remove unnecessary code (#15296) 2018-09-03 13:01:12 +05:30
rohitwaghchaure
5f79479bd0 Merge pull request #15282 from rohitwaghchaure/budget_should_check_on_submit_document
[Minor] Don't validate on cancelled budgets
2018-08-31 16:59:56 +05:30
Rohit Waghchaure
12b4096e68 [Minor] Don't validate on cancelled budgest 2018-08-31 16:54:59 +05:30
Saurabh
1ef72c0b66 Merge branch 'hotfix' 2018-08-31 11:29:13 +05:30
Saurabh
648191b9e8 bumped to version 10.1.50 2018-08-31 11:59:13 +06:00
rohitwaghchaure
624927bf0b [Fix] Not able to export the Supplier-Wise Sales Analytics report (#15271) 2018-08-30 19:19:14 +05:30
Shreya Shah
3df2639a32 Optimize patch (#15263) 2018-08-30 17:24:03 +05:30
Nabin Hait
d982e8fcda Merge branch 'hotfix' 2018-08-29 13:13:54 +05:30
Nabin Hait
af497ef669 bumped to version 10.1.49 2018-08-29 13:43:53 +06:00
Saurabh
79f3ea4094 [minor][fix] call update_percent_complete via validate (#15235) 2018-08-28 14:01:34 +05:30
Shreya Shah
e0a47aeb2f Improve performance for warehouse tree (#15207)
* Fix conflicts

* Modify query structure

* Remove whitespaces
2018-08-28 13:46:22 +05:30
Ahmed Madi
1516b8d54a Update asset_category.js (#15222) 2018-08-27 12:04:12 +05:30
rohitwaghchaure
e33047250e [Fix] Speacial character issue in searching project in sales invoice (#15220) 2018-08-27 11:43:57 +05:30
Faris Ansari
c4e38ffd4d Remove hub page (#15195) 2018-08-23 11:09:31 +05:30
Shreya Shah
be7703387d Fetch reserved qty (#15182)
* Fetch reserved qty

* Update get_item_details.py
2018-08-22 14:45:22 +05:30
rohitwaghchaure
b800382abf [Fix] Due to incorrect conversion factor user not able to make purchase return entry (#15163) 2018-08-20 17:50:31 +05:30
rohitwaghchaure
e689a903d8 Allowed data import for Assessment Result (#15168) 2018-08-20 17:50:04 +05:30
rohitwaghchaure
ecc6af9d2a [Fix] Total materials consumed cost not consider in the calculation of Gross Margin in project (#15171) 2018-08-20 17:39:54 +05:30
rohitwaghchaure
5c5688b374 Added provision to disabled in stock in products settings (#15133) 2018-08-16 10:33:28 +05:30
Shreya Shah
80f30094fb Fix error msg (#15146) 2018-08-16 10:29:16 +05:30
rohitwaghchaure
6fb2117767 Allow to make budget for the same cost center with different account (#15149) 2018-08-16 10:22:06 +05:30
Shreya Shah
0631aed3d9 Discount amount field in all transactions' child tables (#15124)
* Add discount_amount and base_discount_amount in Sales Invoice Item

* Add patch for existing documents

* Discount amount field in all child tables of Purchase and Selling

* Remove console statements
2018-08-14 10:51:48 +05:30
rohitwaghchaure
88b0a1305f Code optimization for accounts receivable report to avoid timeout error (#15114)
* Code optimization for accounts receivable report to avoid timeout error

* Added index for party_type
2018-08-14 10:51:13 +05:30
Saurabh
9e98fdfc6e Merge branch 'hotfix' 2018-08-13 13:15:56 +05:30
Saurabh
cd6b955b99 bumped to version 10.1.48 2018-08-13 13:45:56 +06:00
Saurabh
bfd26b7084 Merge pull request #15131 from rohitwaghchaure/project_task_custom_field_not_copying
Project task custom field not copying and error on project
2018-08-13 12:15:40 +05:30
Rohit Waghchaure
18960c7757 Project task custom field not copying and error on project 2018-08-13 11:40:11 +05:30
Saurabh
9a7838265d Merge branch 'hotfix' 2018-08-10 17:07:01 +05:30
Saurabh
57d3c93e6b bumped to version 10.1.47 2018-08-10 17:37:01 +06:00
rohitwaghchaure
a0b846fb88 [Minor] Added condition for get_bank_cash_account (#15112) 2018-08-09 15:51:38 +05:30
rohitwaghchaure
8356d4b892 [Fix] disable_rounded_total not found error (#15113) 2018-08-09 15:51:11 +05:30
rohitwaghchaure
ba62013cbb [Fix] Not able to delete the task even if task removed from the project (#15105)
* [Fix] Not able to delete the task even if task removed from the project

* [Fix] Custom field's data in task not updating from the project
2018-08-09 15:50:23 +05:30
rohitwaghchaure
59de1dae6a [Fix] Math domain error (#15107) 2018-08-09 10:58:37 +05:30
Saurabh
91cecf8eac Merge branch 'hotfix' 2018-08-07 12:55:51 +05:30
Saurabh
6ec558bd59 bumped to version 10.1.46 2018-08-07 13:25:51 +06:00
Shreya Shah
11dd3ffdb5 Force delete item on deletion of Lab test template (#15079) 2018-08-06 14:43:00 +05:30
Shreya Shah
f0ef673498 Accounts receivable aging based on supplier due date (#15080)
* Accounts receivable aging based on supplier due date (#13801)

* Remove spaces
2018-08-06 14:42:35 +05:30
rohitwaghchaure
57d07d6b8e Merge pull request #15097 from rohitwaghchaure/incorrect_tax_amount_in_report
[Fix] Incorrect tax amount calculation because of same fieldname in the code
2018-08-06 14:32:44 +05:30
rohitwaghchaure
fc682c86c6 [Fix] Inactive student validation issue while disabling the student group (#15095) 2018-08-06 14:29:46 +05:30
Rohit Waghchaure
15f52e7b71 [Fix] Incorrect tax amount calculation because of same fieldname in the code 2018-08-06 14:29:34 +05:30
Ameya Shenoy
26bfede116 Merge pull request #15078 from rohitwaghchaure/task_email_alert_not_working_from_project
Fix] Task's email alert on status change not working if we change the task status from the project form
2018-08-06 12:33:07 +05:30
Rohit Waghchaure
86217ca96c [Fix] Task email alert on status change not working if we change the task status from the project form 2018-08-02 16:41:53 +05:30
rohitwaghchaure
ae4ff5a403 [Fix] In words showing grand total instead of rounded total (#15065) 2018-08-01 18:09:51 +05:30
rohitwaghchaure
0452b405f4 [Fix] Not able to submit the expense claim (#15057) 2018-08-01 17:45:51 +05:30
rohitwaghchaure
dc7004e083 [Fix] Patient Appointment Calendar not showing data properly (#15052) 2018-07-31 16:01:02 +05:30
108 changed files with 3573 additions and 4941 deletions

View File

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

View File

@@ -207,10 +207,13 @@ def validate_account_number(name, account_number, company):
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None):
account = frappe.db.get_value("Account", name, ["company"], as_dict=True)
account = frappe.db.get_value("Account", name, "company", as_dict=True)
if not account: return
validate_account_number(name, account_number, account.company)
if account_number:
frappe.db.set_value("Account", name, "account_number", account_number.strip())
else:
frappe.db.set_value("Account", name, "account_number", "")
frappe.db.set_value("Account", name, "account_name", account_name.strip())
new_name = get_account_autoname(account_number, account_name, account.company)

View File

@@ -27,13 +27,21 @@ class Budget(Document):
def validate_duplicate(self):
budget_against_field = frappe.scrub(self.budget_against)
budget_against = self.get(budget_against_field)
existing_budget = frappe.db.get_value("Budget", {budget_against_field: budget_against,
"fiscal_year": self.fiscal_year, "company": self.company,
"name": ["!=", self.name], "docstatus": ["!=", 2]})
if existing_budget:
frappe.throw(_("Another Budget record '{0}' already exists against {1} '{2}' for fiscal year {3}")
.format(existing_budget, self.budget_against, budget_against, self.fiscal_year), DuplicateBudgetError)
accounts = [d.account for d in self.accounts] or []
existing_budget = frappe.db.sql("""
select
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
where
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
b.fiscal_year=%s and b.name != %sand ba.account in (%s) """
% ('%s', budget_against_field, '%s', '%s', '%s', ','.join(['%s'] * len(accounts))),
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts), as_dict=1)
for d in existing_budget:
frappe.throw(_("Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}")
.format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year), DuplicateBudgetError)
def validate_accounts(self):
account_list = []
for d in self.get('accounts'):

View File

@@ -132,7 +132,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -702,7 +702,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
}
@@ -718,7 +718,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-20 12:40:09.611951",
"modified": "2018-08-10 16:16:53.019380",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",
@@ -794,4 +794,4 @@
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}
}

View File

@@ -381,7 +381,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -1443,7 +1443,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-08-31 11:21:09.442695",
"modified": "2018-08-10 16:35:31.361030",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -199,7 +199,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -661,7 +661,7 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -795,7 +795,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-07 19:54:19.851534",
"modified": "2018-08-10 16:35:42.833549",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@@ -286,7 +286,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -1439,7 +1439,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -1791,7 +1791,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-07-27 01:49:24.720317",
"modified": "2018-08-10 16:34:46.771275",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -21,8 +21,6 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
if(!this.frm.doc.supplier && this.frm.doc.credit_to) {
this.frm.set_df_property("credit_to", "print_hide", 0);
}
} else {
this.frm.set_value("disable_rounded_total", cint(frappe.sys_defaults.disable_rounded_total));
}
},

View File

@@ -388,16 +388,20 @@ class PurchaseInvoice(BuyingController):
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
warehouse_account = get_warehouse_account_map()
voucher_wise_stock_value = {}
if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
# warehouse account
warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
* flt(item.qty) * flt(item.conversion_factor), item.precision("base_net_amount"))
warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries,
item, voucher_wise_stock_value, account_currency)
gl_entries.append(
self.get_gl_dict({
@@ -469,6 +473,36 @@ class PurchaseInvoice(BuyingController):
self.negative_expense_to_be_booked += flt(item.item_tax_amount, \
item.precision("item_tax_amount"))
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
net_amt_precision = item.precision("base_net_amount")
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
* flt(item.qty) * flt(item.conversion_factor), net_amt_precision)
# Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and
warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)):
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append(
self.get_gl_dict({
"account": cost_of_goods_sold_account,
"against": item.expense_account,
"debit": stock_adjustment_amt,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
"project": item.project
}, account_currency)
)
warehouse_debit_amount = stock_amount
return warehouse_debit_amount
def make_tax_gl_entries(self, gl_entries):
# tax table gl entries
valuation_tax = {}

View File

@@ -585,6 +585,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2156,7 +2188,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-11-30 14:21:00.962126",
"modified": "2018-08-06 05:17:38.205356",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -506,7 +506,7 @@ def save_invoice(doc, name, name_list):
frappe.db.commit()
name_list.append(name)
except Exception:
frappe.log_error(frappe.get_traceback())
frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
return name_list

View File

@@ -32,7 +32,7 @@ frappe.query_reports["Accounts Payable"] = {
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date',
"options": 'Posting Date\nDue Date\nSupplier Invoice Date',
"default": "Posting Date"
},
{

View File

@@ -161,21 +161,21 @@
</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"]) %}</td>
{%= format_currency(data[i]["invoiced_amount"], 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>
{%= 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>
{%= 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>
{%= 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>
{%= data[i]["po_no"] %}</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>
@@ -189,15 +189,15 @@
{% } %}
<td><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"] ) %}</td>
{%= format_currency(data[i]["invoiced_amount"], 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>
{%= 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>
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
@@ -238,4 +238,4 @@
{% } %}
</tbody>
</table>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>

View File

@@ -60,6 +60,7 @@ class ReceivablePayableReport(object):
for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
columns.append({
"label": label,
"fieldname": frappe.scrub(label),
"fieldtype": "Currency",
"options": "currency",
"width": 120
@@ -103,9 +104,13 @@ class ReceivablePayableReport(object):
]
if args.get('party_type') == 'Customer':
columns += [_("Customer LPO") + ":Data:100"]
columns.append({
"label": _("Customer LPO"),
"fieldtype": "Data",
"fieldname": "po_no",
"width": 100,
})
columns += [_("Delivery Note") + ":Data:100"]
if args.get("party_type") == "Customer":
columns += [
_("Territory") + ":Link/Territory:80",
@@ -123,9 +128,6 @@ class ReceivablePayableReport(object):
currency_precision = get_currency_precision() or 2
dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
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"))
if not self.filters.get("company"):
@@ -138,7 +140,14 @@ class ReceivablePayableReport(object):
data = []
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")):
gl_entries_data = self.get_entries_till(self.filters.report_date, args.get("party_type"))
if gl_entries_data:
voucher_nos = [d.voucher_no for d in gl_entries_data] or []
dn_details = get_dn_details(args.get("party_type"), voucher_nos)
voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
for gle in gl_entries_data:
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
self.filters.report_date, dr_or_cr, return_entries, currency_precision)
@@ -151,6 +160,7 @@ class ReceivablePayableReport(object):
# get due date
due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
bill_date = voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
row += [gle.voucher_type, gle.voucher_no, due_date]
@@ -167,15 +177,25 @@ class ReceivablePayableReport(object):
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
# ageing data
entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date
if self.filters.ageing_based_on == "Due Date":
entry_date = due_date
elif self.filters.ageing_based_on == "Supplier Invoice Date":
entry_date = bill_date
else:
entry_date = gle.posting_date
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
# issue 6371-Ageing buckets should not have amounts if due date is not reached
if self.filters.ageing_based_on == "Due Date" \
and getdate(due_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.ageing_based_on == "Supplier Invoice Date" \
and getdate(bill_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
else:
@@ -207,12 +227,11 @@ class ReceivablePayableReport(object):
def get_entries_after(self, report_date, party_type):
# returns a distinct list
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries(party_type)
if getdate(e.posting_date) > report_date]))
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries(party_type, report_date, for_future=True)]))
def get_entries_till(self, report_date, party_type):
# returns a generator
return (e for e in self.get_gl_entries(party_type) if getdate(e.posting_date) <= report_date)
return self.get_gl_entries(party_type, report_date)
def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers):
return (
@@ -239,14 +258,16 @@ class ReceivablePayableReport(object):
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
amount = flt(e.get(reverse_dr_or_cr), currency_precision) - flt(e.get(dr_or_cr), currency_precision)
if e.voucher_no not in return_entries:
payment_amount += amount
else:
credit_note_amount += amount
outstanding_amount = flt((flt(gle.get(dr_or_cr)) - flt(gle.get(reverse_dr_or_cr)) \
- payment_amount - credit_note_amount), currency_precision)
outstanding_amount = (flt((flt(gle.get(dr_or_cr), currency_precision)
- flt(gle.get(reverse_dr_or_cr), currency_precision)
- payment_amount - credit_note_amount), currency_precision))
credit_note_amount = flt(credit_note_amount, currency_precision)
return outstanding_amount, credit_note_amount
@@ -275,42 +296,31 @@ class ReceivablePayableReport(object):
return self.party_map
def get_voucher_details(self, party_type, dn_details):
voucher_details = frappe._dict()
def get_gl_entries(self, party_type, date=None, for_future=False):
conditions, values = self.prepare_conditions(party_type)
if party_type == "Customer":
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 self.filters.get(scrub(party_type)):
select_fields = "sum(debit_in_account_currency) as debit, sum(credit_in_account_currency) as credit"
else:
select_fields = "sum(debit) as debit, sum(credit) as credit"
if party_type == "Supplier":
for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date
from `tabPurchase Invoice` where docstatus = 1
union
select name, due_date, bill_no, bill_date from `tabJournal Entry`
where docstatus = 1 and bill_no is not NULL""", as_dict=1):
voucher_details.setdefault(pi.name, pi)
if date and not for_future:
conditions += " and posting_date <= '%s'" % date
return voucher_details
if date and for_future:
conditions += " and posting_date > '%s'" % date
def get_gl_entries(self, party_type):
if not hasattr(self, "gl_entries"):
conditions, values = self.prepare_conditions(party_type)
if self.filters.get(scrub(party_type)):
select_fields = "sum(debit_in_account_currency) as debit, sum(credit_in_account_currency) as credit"
else:
select_fields = "sum(debit) as debit, sum(credit) as credit"
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
voucher_type, voucher_no, against_voucher_type, against_voucher,
account_currency, remarks, {0}
from `tabGL Entry`
where docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
self.gl_entries = frappe.db.sql("""
select
name, posting_date, account, party_type, party, voucher_type, voucher_no,
against_voucher_type, against_voucher, account_currency, remarks, {0}
from
`tabGL Entry`
where
docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
group by voucher_type, voucher_no, against_voucher_type, against_voucher, party
order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True)
.format(select_fields, conditions), values, as_dict=True)
return self.gl_entries
@@ -446,18 +456,57 @@ def get_pdc_details(party_type, report_date):
return pdc_details
def get_dn_details(party_type):
def get_dn_details(party_type, voucher_nos):
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):
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 != ''
and parent in (%s) group by parent
""" %(','.join(['%s'] * len(voucher_nos))), tuple(voucher_nos) , as_dict=1):
dn_details.setdefault(si.parent, si.dn)
for si in frappe.db.sql("""
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 != ''
and against_sales_invoice in (%s)
group by against_sales_invoice
""" %(','.join(['%s'] * len(voucher_nos))), tuple(voucher_nos) , as_dict=1):
if si.parent in dn_details:
dn_details[si.parent] += ', %s' %(si.dn)
else:
dn_details.setdefault(si.parent, si.dn)
return dn_details
def get_voucher_details(party_type, voucher_nos, dn_details):
voucher_details = frappe._dict()
if party_type == "Customer":
for si in frappe.db.sql("""select name, due_date, po_no
from `tabSales Invoice` where docstatus=1 and name in (%s)
""" %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
si['delivery_note'] = dn_details.get(si.name)
voucher_details.setdefault(si.name, si)
if party_type == "Supplier":
for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date
from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
""" %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
voucher_details.setdefault(pi.name, pi)
for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date from
`tabJournal Entry` where docstatus = 1 and bill_no is not NULL and name in (%s)
""" %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
voucher_details.setdefault(pi.name, pi)
return voucher_details

View File

@@ -3,47 +3,75 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt
from frappe import _
def execute(filters=None):
columns, data = get_columns(), get_data(filters)
return columns, data
def get_data(filters):
data = frappe.db.sql("""
select
a.name as asset, a.asset_category, a.status,
a.depreciation_method, a.purchase_date, a.gross_purchase_amount,
ds.schedule_date as depreciation_date, ds.depreciation_amount,
ds.accumulated_depreciation_amount,
(a.gross_purchase_amount - ds.accumulated_depreciation_amount) as amount_after_depreciation,
ds.journal_entry as depreciation_entry
from
`tabAsset` a, `tabDepreciation Schedule` ds
where
a.name = ds.parent
and a.docstatus=1
and ifnull(ds.journal_entry, '') != ''
and ds.schedule_date between %(from_date)s and %(to_date)s
and a.company = %(company)s
{conditions}
order by
a.name asc, ds.schedule_date asc
""".format(conditions=get_filter_conditions(filters)), filters, as_dict=1)
return data
def get_filter_conditions(filters):
conditions = ""
data = []
depreciation_accounts = frappe.db.sql_list(""" select name from tabAccount
where ifnull(account_type, '') = 'Depreciation' """)
filters_data = [["company", "=", filters.get('company')],
["posting_date", ">=", filters.get('from_date')],
["posting_date", "<=", filters.get('to_date')],
["against_voucher_type", "=", "Asset"],
["account", "in", depreciation_accounts]]
if filters.get("asset"):
conditions += " and a.name = %(asset)s"
filters_data.append(["against_voucher", "=", filters.get("asset")])
if filters.get("asset_category"):
conditions += " and a.asset_category = %(asset_category)s"
return conditions
assets = frappe.db.sql_list("""select name from tabAsset
where asset_category = %s and docstatus=1""", filters.get("asset_category"))
filters_data.append(["against_voucher", "in", assets])
gl_entries = frappe.get_all('GL Entry',
filters= filters_data,
fields = ["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"],
order_by= "against_voucher, posting_date")
if not gl_entries:
return data
assets = [d.against_voucher for d in gl_entries]
assets_details = get_assets_details(assets)
for d in gl_entries:
asset_data = assets_details.get(d.against_voucher)
if not asset_data.get("accumulated_depreciation_amount"):
asset_data.accumulated_depreciation_amount = d.debit
else:
asset_data.accumulated_depreciation_amount += d.debit
row = frappe._dict(asset_data)
row.update({
"depreciation_amount": d.debit,
"depreciation_date": d.posting_date,
"amount_after_depreciation": (flt(row.gross_purchase_amount) -
flt(row.accumulated_depreciation_amount)),
"depreciation_entry": d.voucher_no
})
data.append(row)
return data
def get_assets_details(assets):
assets_details = {}
fields = ["name as asset", "gross_purchase_amount",
"asset_category", "status", "depreciation_method", "purchase_date"]
for d in frappe.get_all("Asset", fields = fields, filters = {'name': ('in', assets)}):
assets_details.setdefault(d.asset, d)
return assets_details
def get_columns():
return [
{

View File

@@ -223,13 +223,13 @@ def get_tax_accounts(item_list, columns, company_currency,
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) \
if item_net_amount else 0
if item_tax_amount:
tax_amount = flt(item_tax_amount, tax_amount_precision)
tax_amount = (tax_amount * -1
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_amount)
tax_value = flt(item_tax_amount, tax_amount_precision)
tax_value = (tax_value * -1
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
"tax_rate": tax_rate,
"tax_amount": tax_amount
"tax_amount": tax_value
})
except ValueError:

View File

@@ -67,11 +67,33 @@ frappe.ui.form.on('Asset', {
frm.trigger("create_asset_maintenance");
}, __("Make"));
}
if (!frm.doc.calculate_depreciation) {
frm.add_custom_button(__("Depreciation Entry"), function() {
frm.trigger("make_journal_entry");
}, __("Make"));
}
frm.page.set_inner_btn_group_as_primary(__("Make"));
frm.trigger("setup_chart");
}
},
make_journal_entry: function(frm) {
frappe.call({
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
args: {
asset_name: frm.doc.name
},
callback: function(r) {
if (r.message) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
})
},
setup_chart: function(frm) {
var x_intervals = [frm.doc.purchase_date];
var asset_values = [frm.doc.gross_purchase_amount];

View File

@@ -283,3 +283,34 @@ def get_item_details(item_code):
})
return ret
@frappe.whitelist()
def make_journal_entry(asset_name):
asset = frappe.get_doc("Asset", asset_name)
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
get_depreciation_accounts(asset)
depreciation_cost_center, depreciation_series = frappe.db.get_value("Company", asset.company,
["depreciation_cost_center", "series_for_depreciation_entry"])
depreciation_cost_center = asset.cost_center or depreciation_cost_center
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Depreciation Entry"
je.naming_series = depreciation_series
je.company = asset.company
je.remark = "Depreciation Entry against asset {0}".format(asset_name)
je.append("accounts", {
"account": depreciation_expense_account,
"reference_type": "Asset",
"reference_name": asset.name,
"cost_center": depreciation_cost_center
})
je.append("accounts", {
"account": accumulated_depreciation_account,
"reference_type": "Asset",
"reference_name": asset.name
})
return je

View File

@@ -5,13 +5,13 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, today, getdate
from frappe.utils import flt, today, getdate, cint
def post_depreciation_entries(date=None):
# Return if automatic booking of asset depreciation is disabled
if not frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically"):
if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
return
if not date:
date = today()
for asset in get_depreciable_assets(date):

View File

@@ -22,7 +22,7 @@ frappe.ui.form.on('Asset Category', {
var d = locals[cdt][cdn];
return {
"filters": {
"root_type": "Asset",
"account_type": "Accumulated Depreciation",
"is_group": 0,
"company": d.company_name
}
@@ -41,4 +41,4 @@ frappe.ui.form.on('Asset Category', {
});
}
});
});

View File

@@ -2378,6 +2378,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rounded_total",
"fieldtype": "Currency",
"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": "Rounded Total",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2409,6 +2440,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disable_rounded_total",
"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": "Disable Rounded Total",
"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,
@@ -3293,7 +3354,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-07-18 07:49:53.131408",
"modified": "2018-08-01 15:18:33.155409",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -629,6 +629,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -1897,7 +1929,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-07-18 07:53:54.677844",
"modified": "2018-08-06 05:16:58.258276",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

View File

@@ -1983,6 +1983,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rounded_total",
"fieldtype": "Currency",
"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": "Rounded Total",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2014,6 +2045,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disable_rounded_total",
"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": "Disable Rounded Total",
"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,
@@ -2177,65 +2238,65 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "group_same_items",
"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": "Group same items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"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,
"fieldname": "group_same_items",
"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": "Group same items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"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": "column_break_72",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_72",
"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": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "letter_head",
"fieldtype": "Link",
"hidden": 0,
@@ -2549,7 +2610,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-07-06 02:45:48.616334",
"modified": "2018-08-01 15:18:23.265621",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",

View File

@@ -60,7 +60,12 @@ class SupplierQuotation(BuyingController):
for rfq in rfq_list:
doc = frappe.get_doc('Request for Quotation', rfq)
doc_sup = frappe.get_all('Request for Quotation Supplier', filters=
{'parent': doc.name, 'supplier': self.supplier}, fields=['name', 'quote_status'])[0]
{'parent': doc.name, 'supplier': self.supplier}, fields=['name', 'quote_status'])
doc_sup = doc_sup[0] if doc_sup else None
if not doc_sup:
frappe.throw(_("Supplier {0} not found in {1}").format(self.supplier,
"<a href='desk#Form/Request for Quotation/{0}'> Request for Quotation {0} </a>".format(doc.name)))
quote_status = _('Received')
for item in doc.items:
@@ -152,4 +157,4 @@ def make_quotation(source_name, target_doc=None):
}
}, target_doc)
return doclist
return doclist

View File

@@ -470,6 +470,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -1645,7 +1677,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-01-25 15:04:40.171617",
"modified": "2018-08-06 05:33:07.404385",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Item",

View File

@@ -291,14 +291,6 @@ def get_data():
"label": _("Healthcare"),
"hidden": 1
},
{
"module_name": "Hub",
"color": "#009248",
"icon": "/assets/erpnext/images/hub_logo.svg",
"type": "page",
"link": "hub",
"label": _("Hub")
},
{
"module_name": "Data Import",
"color": "#FFF168",

View File

@@ -1,24 +0,0 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return [
{
"label": _("Setup"),
"items": [
{
"type": "doctype",
"name": "Hub Settings"
},
]
},
{
"label": _("Hub"),
"items": [
{
"type": "page",
"name": "hub"
},
]
},
]

View File

@@ -99,9 +99,16 @@ class BuyingController(StockController):
def set_total_in_words(self):
from frappe.utils import money_in_words
if self.meta.get_field("base_in_words"):
self.base_in_words = money_in_words(self.base_grand_total, self.company_currency)
amount = (self.base_rounded_total
if not self.get("disable_rounded_total") else self.base_grand_total)
self.base_in_words = money_in_words(amount, self.company_currency)
if self.meta.get_field("in_words"):
self.in_words = money_in_words(self.grand_total, self.currency)
amount = (self.rounded_total
if not self.get("disable_rounded_total") else self.grand_total)
self.in_words = money_in_words(amount, self.currency)
# update valuation rate
def update_valuation_rate(self, parentfield):

View File

@@ -218,7 +218,8 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
cond = ''
if filters.get('customer'):
cond = '(`tabProject`.customer = "' + filters['customer'] + '" or ifnull(`tabProject`.customer,"")="") and'
cond = """(`tabProject`.customer = '%s' or
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
return frappe.db.sql("""select `tabProject`.name from `tabProject`
where `tabProject`.status not in ("Completed", "Cancelled")

View File

@@ -139,7 +139,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
.format(args.item_code), StockOverReturnError)
elif abs(current_stock_qty) > max_returnable_qty:
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
.format(args.idx, reference_qty, args.item_code), StockOverReturnError)
.format(args.idx, max_returnable_qty, args.item_code), StockOverReturnError)
def get_ref_item_dict(valid_items, ref_item_row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -151,6 +151,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
"rejected_qty": 0,
"received_qty": 0,
"serial_no": [],
"conversion_factor": ref_item_row.get("conversion_factor", 1),
"batch_no": []
}))
item_dict = valid_items[ref_item_row.item_code]

View File

@@ -1,7 +1,7 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "RES.######",
"beta": 0,
@@ -662,7 +662,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-01-09 14:14:30.090317",
"modified": "2018-08-20 11:21:19.248393",
"modified_by": "Administrator",
"module": "Education",
"name": "Assessment Result",

View File

@@ -33,7 +33,7 @@ class StudentGroup(Document):
program_enrollment = get_program_enrollment(self.academic_year, self.academic_term, self.program, self.batch, self.course)
students = [d.student for d in program_enrollment] if program_enrollment else []
for d in self.students:
if not frappe.db.get_value("Student", d.student, "enabled") and d.active:
if not frappe.db.get_value("Student", d.student, "enabled") and d.active and not self.disabled:
frappe.throw(_("{0} - {1} is inactive student".format(d.group_roll_number, d.student_name)))
if (self.group_based_on == "Batch") and cint(frappe.defaults.get_defaults().validate_batch)\

View File

@@ -34,7 +34,7 @@ class LabTestTemplate(Document):
# remove template refernce from item and disable item
if(self.item):
try:
frappe.delete_doc("Item",self.item)
frappe.delete_doc("Item",self.item, force=True)
except Exception:
frappe.throw("""Not permitted. Please disable the Test Template""")

View File

@@ -111,6 +111,7 @@ frappe.ui.form.on('Patient Appointment', {
frm.set_value('appointment_time', selected_slot);
frm.set_value('duration', data.time_per_appointment);
d.hide();
frm.enable_save();
frm.save();
}
});

View File

@@ -292,10 +292,10 @@ def get_events(start, end, filters=None):
conditions = get_event_conditions("Patient Appointment", filters)
data = frappe.db.sql("""select name, patient, physician, status,
duration, timestamp(appointment_date, appointment_time) as
'start' from `tabPatient Appointment` where
'appointment_date' from `tabPatient Appointment` where
(appointment_date between %(start)s and %(end)s)
and docstatus < 2 {conditions}""".format(conditions=conditions),
{"start": start, "end": end}, as_dict=True, update={"allDay": 0})
for item in data:
item.end = item.start + datetime.timedelta(minutes = item.duration)
item.appointment_datetime = item.appointment_date + datetime.timedelta(minutes = item.duration)
return data

View File

@@ -45,14 +45,21 @@ class Employee(NestedSet):
self.validate_prefered_email()
if self.user_id:
self.validate_for_enabled_user_id()
self.validate_duplicate_user_id()
self.validate_user_details()
else:
existing_user_id = frappe.db.get_value("Employee", self.name, "user_id")
if existing_user_id:
frappe.permissions.remove_user_permission(
"Employee", self.name, existing_user_id)
def validate_user_details(self):
data = frappe.db.get_value('User',
self.user_id, ['enabled', 'user_image'], as_dict=1)
self.image = data.get("user_image")
self.validate_for_enabled_user_id(data.get("enabled", 0))
self.validate_duplicate_user_id()
def update_nsm_model(self):
frappe.utils.nestedset.update_nsm(self)
@@ -133,10 +140,10 @@ class Employee(NestedSet):
if self.status == 'Left' and not self.relieving_date:
throw(_("Please enter relieving date."))
def validate_for_enabled_user_id(self):
def validate_for_enabled_user_id(self, enabled):
if not self.status == 'Active':
return
enabled = frappe.db.get_value("User", self.user_id, "enabled")
if enabled is None:
frappe.throw(_("User {0} does not exist").format(self.user_id))
if enabled == 0:

View File

@@ -71,19 +71,22 @@ frappe.ui.form.on('Employee Loan', {
}
})
},
mode_of_payment: function (frm) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
args: {
"mode_of_payment": frm.doc.mode_of_payment,
"company": frm.doc.company
},
callback: function (r, rt) {
if (r.message) {
frm.set_value("payment_account", r.message.account);
if (frm.doc.mode_of_payment && frm.doc.company) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
args: {
"mode_of_payment": frm.doc.mode_of_payment,
"company": frm.doc.company
},
callback: function (r, rt) {
if (r.message) {
frm.set_value("payment_account", r.message.account);
}
}
}
});
});
}
},
employee_loan_application: function (frm) {

View File

@@ -29,8 +29,13 @@ class EmployeeLoanApplication(Document):
if self.repayment_method == "Repay Fixed Amount per Period":
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
monthly_interest_amount = self.loan_amount * monthly_interest_rate
if monthly_interest_amount >= self.repayment_amount:
frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}").
format(self.repayment_amount, monthly_interest_amount))
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) /
math.log(self.repayment_amount - (monthly_interest_amount))) /
(math.log(1 + monthly_interest_rate)))
else:
self.repayment_periods = self.loan_amount / self.repayment_amount

View File

@@ -30,7 +30,7 @@ class ExpenseClaim(AccountsController):
self.validate_expense_approver()
self.calculate_total_amount()
set_employee_name(self)
self.set_expense_account()
self.set_expense_account(validate=True)
self.set_payable_account()
self.set_cost_center()
self.set_status()
@@ -224,10 +224,11 @@ class ExpenseClaim(AccountsController):
self.total_advance_amount += flt(d.allocated_amount)
if self.total_advance_amount:
if flt(self.total_advance_amount) > flt(self.total_claimed_amount):
precision = self.precision("total_advance_amount")
if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision):
frappe.throw(_("Total advance amount cannot be greater than total claimed amount"))
if self.total_sanctioned_amount \
and flt(self.total_advance_amount) > flt(self.total_sanctioned_amount):
and flt(self.total_advance_amount, precision) > flt(self.total_sanctioned_amount, precision):
frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount"))
def validate_sanctioned_amount(self):
@@ -235,9 +236,9 @@ class ExpenseClaim(AccountsController):
if flt(d.sanctioned_amount) > flt(d.claim_amount):
frappe.throw(_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx))
def set_expense_account(self):
def set_expense_account(self, validate=False):
for expense in self.expenses:
if not expense.default_account:
if not expense.default_account or not validate:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
def update_reimbursed_amount(doc):

View File

@@ -113,20 +113,17 @@ frappe.ui.form.on('Salary Slip Timesheet', {
// Get leave details
//---------------------------------------------------------------------
var get_emp_and_leave_details = function(doc, dt, dn) {
if(!doc.start_date){
return frappe.call({
method: 'get_emp_and_leave_details',
doc: locals[dt][dn],
callback: function(r, rt) {
cur_frm.refresh();
calculate_all(doc, dt, dn);
}
});
}
return frappe.call({
method: 'get_emp_and_leave_details',
doc: locals[dt][dn],
callback: function(r, rt) {
cur_frm.refresh();
calculate_all(doc, dt, dn);
}
});
}
cur_frm.cscript.employee = function(doc,dt,dn){
doc.salary_structure = ''
get_emp_and_leave_details(doc, dt, dn);
}
@@ -249,4 +246,4 @@ var total_work_hours = function(frm, dt, dn) {
frm.refresh_field('gross_pay');
calculate_net_pay(frm.doc, dt, dn);
});
}
}

View File

@@ -181,7 +181,8 @@ class SalarySlip(TransactionBase):
if len(st_name) > 1:
frappe.msgprint(_("Multiple active Salary Structures found for employee {0} for the given dates")
.format(self.employee), title=_('Warning'))
return st_name and st_name[0][0] or ''
self.salary_structure = st_name and st_name[0][0] or ''
return self.salary_structure
else:
self.salary_structure = None
frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")

View File

@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, add_days, date_diff
from frappe.utils import cstr, add_days, date_diff, getdate
from frappe import _
from frappe.utils.csvutils import UnicodeWriter
from frappe.model.document import Document
@@ -48,8 +48,9 @@ def add_data(w, args):
for employee in employees:
existing_attendance = {}
if existing_attendance_records \
and tuple([date, employee.name]) in existing_attendance_records:
existing_attendance = existing_attendance_records[tuple([date, employee.name])]
and tuple([getdate(date), employee.name]) in existing_attendance_records:
existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])]
row = [
existing_attendance and existing_attendance.name or "",
employee.name, employee.employee_name, date,
@@ -114,6 +115,7 @@ def upload():
if not row: continue
row_idx = i + 5
d = frappe._dict(zip(columns, row))
d["doctype"] = "Attendance"
if d.name:
d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus")
@@ -121,6 +123,8 @@ def upload():
try:
check_record(d)
ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True))
except AttributeError:
pass
except Exception as e:
error = True
ret.append('Error for row (#%d) %s : %s' % (row_idx,

View File

@@ -1,184 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, requests, json
from frappe.utils import now, nowdate, cint
from frappe.utils.nestedset import get_root_of
from frappe.contacts.doctype.contact.contact import get_default_contact
@frappe.whitelist()
def enable_hub():
hub_settings = frappe.get_doc('Hub Settings')
hub_settings.register()
frappe.db.commit()
return hub_settings
@frappe.whitelist()
def get_items(start=0, limit=20, category=None, order_by=None, company=None, text=None):
connection = get_client_connection()
filters = {
'hub_category': category,
}
if text:
filters.update({'item_name': ('like', '%' + text + '%')})
if company:
filters.update({'company_name': company})
response = connection.get_list('Hub Item',
limit_start=start, limit_page_length=limit,
filters=filters)
return response
@frappe.whitelist()
def get_categories():
connection = get_client_connection()
response = connection.get_list('Hub Category')
return response
@frappe.whitelist()
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)
@frappe.whitelist()
def get_company_details(hub_sync_id):
connection = get_client_connection()
return connection.get_doc('Hub Company', hub_sync_id)
def get_client_connection():
# frappeclient connection
hub_connection = get_hub_connection()
return hub_connection.connection
def get_hub_connection():
hub_connector = frappe.get_doc(
'Data Migration Connector', 'Hub Connector')
hub_connection = hub_connector.get_connection()
return hub_connection
def make_opportunity(buyer_name, email_id):
buyer_name = "HUB-" + buyer_name
if not frappe.db.exists('Lead', {'email_id': email_id}):
lead = frappe.new_doc("Lead")
lead.lead_name = buyer_name
lead.email_id = email_id
lead.save(ignore_permissions=True)
o = frappe.new_doc("Opportunity")
o.enquiry_from = "Lead"
o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"]
o.save(ignore_permissions=True)
@frappe.whitelist()
def make_rfq_and_send_opportunity(item, supplier):
supplier = make_supplier(supplier)
contact = make_contact(supplier)
item = make_item(item)
rfq = make_rfq(item, supplier, contact)
status = send_opportunity(contact)
return {
'rfq': rfq,
'hub_document_created': status
}
def make_supplier(supplier):
# make supplier if not already exists
supplier = frappe._dict(json.loads(supplier))
if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}):
supplier_doc = frappe.get_doc({
'doctype': 'Supplier',
'supplier_name': supplier.supplier_name,
'supplier_type': supplier.supplier_type,
'supplier_email': supplier.supplier_email
}).insert()
else:
supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name)
return supplier_doc
def make_contact(supplier):
contact_name = get_default_contact('Supplier', supplier.supplier_name)
# make contact if not already exists
if not contact_name:
contact = frappe.get_doc({
'doctype': 'Contact',
'first_name': supplier.supplier_name,
'email_id': supplier.supplier_email,
'is_primary_contact': 1,
'links': [
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
]
}).insert()
else:
contact = frappe.get_doc('Contact', contact_name)
return contact
def make_item(item):
# make item if not already exists
item = frappe._dict(json.loads(item))
if not frappe.db.exists('Item', {'item_code': item.item_code}):
item_doc = frappe.get_doc({
'doctype': 'Item',
'item_code': item.item_code,
'item_group': item.item_group,
'is_item_from_hub': 1
}).insert()
else:
item_doc = frappe.get_doc('Item', item.item_code)
return item_doc
def make_rfq(item, supplier, contact):
# make rfq
rfq = frappe.get_doc({
'doctype': 'Request for Quotation',
'transaction_date': nowdate(),
'status': 'Draft',
'company': frappe.db.get_single_value('Hub Settings', 'company'),
'message_for_supplier': 'Please supply the specified items at the best possible rates',
'suppliers': [
{ 'supplier': supplier.name, 'contact': contact.name }
],
'items': [
{
'item_code': item.item_code,
'qty': 1,
'schedule_date': nowdate(),
'warehouse': item.default_warehouse or get_root_of("Warehouse"),
'description': item.description,
'uom': item.stock_uom
}
]
}).insert()
rfq.save()
rfq.submit()
return rfq
def send_opportunity(contact):
# Make Hub Message on Hub with lead data
doc = {
'doctype': 'Lead',
'lead_name': frappe.db.get_single_value('Hub Settings', 'company'),
'email_id': frappe.db.get_single_value('Hub Settings', 'user')
}
args = frappe._dict(dict(
doctype='Hub Message',
reference_doctype='Lead',
data=json.dumps(doc),
user=contact.email_id
))
connection = get_hub_connection()
response = connection.insert('Hub Message', args)
return response.ok

View File

@@ -1,45 +0,0 @@
{
"condition": "{'name': ('=', frappe.db.get_single_value('Hub Settings', 'company'))}",
"creation": "2017-09-07 11:38:43.169065",
"docstatus": 0,
"doctype": "Data Migration Mapping",
"fields": [
{
"is_child_table": 0,
"local_fieldname": "name",
"remote_fieldname": "company_name"
},
{
"is_child_table": 0,
"local_fieldname": "country",
"remote_fieldname": "country"
},
{
"is_child_table": 0,
"local_fieldname": "\"city\"",
"remote_fieldname": "seller_city"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.local.site",
"remote_fieldname": "site_name"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.session.user",
"remote_fieldname": "user"
}
],
"idx": 2,
"local_doctype": "Company",
"mapping_name": "Company to Hub Company",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2017-10-09 17:30:17.853929",
"modified_by": "Administrator",
"name": "Company to Hub Company",
"owner": "Administrator",
"page_length": 10,
"remote_objectname": "Hub Company",
"remote_primary_key": "name"
}

View File

@@ -1,29 +0,0 @@
import frappe, json
def pre_process(doc):
return json.loads(doc['data'])
def post_process(remote_doc=None, local_doc=None, **kwargs):
if not local_doc:
return
hub_message = remote_doc
# update hub message on hub
hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
connection = hub_connector.get_connection()
connection.update('Hub Message', dict(
status='Synced'
), hub_message['name'])
# make opportunity after lead is created
lead = local_doc
opportunity = frappe.get_doc({
'doctype': 'Opportunity',
'naming_series': 'OPTY-',
'opportunity_type': 'Hub',
'enquiry_from': 'Lead',
'status': 'Open',
'lead': lead.name,
'company': lead.company,
'transaction_date': frappe.utils.today()
}).insert()

View File

@@ -1,31 +0,0 @@
{
"condition": "{'reference_doctype': 'Lead', 'user': frappe.db.get_single_value('Hub Settings', 'user'), 'status': 'Pending'}",
"creation": "2017-09-20 15:06:40.279930",
"docstatus": 0,
"doctype": "Data Migration Mapping",
"fields": [
{
"is_child_table": 0,
"local_fieldname": "email_id",
"remote_fieldname": "email_id"
},
{
"is_child_table": 0,
"local_fieldname": "lead_name",
"remote_fieldname": "lead_name"
}
],
"idx": 0,
"local_doctype": "Lead",
"local_primary_key": "email_id",
"mapping_name": "Hub Message to Lead",
"mapping_type": "Pull",
"migration_id_field": "hub_sync_id",
"modified": "2017-10-09 17:30:17.908830",
"modified_by": "Administrator",
"name": "Hub Message to Lead",
"owner": "frappetest@gmail.com",
"page_length": 10,
"remote_objectname": "Hub Message",
"remote_primary_key": "name"
}

View File

@@ -1,55 +0,0 @@
{
"condition": "{\"publish_in_hub\": 1}",
"creation": "2017-09-07 13:27:52.726350",
"docstatus": 0,
"doctype": "Data Migration Mapping",
"fields": [
{
"is_child_table": 0,
"local_fieldname": "item_code",
"remote_fieldname": "item_code"
},
{
"is_child_table": 0,
"local_fieldname": "item_name",
"remote_fieldname": "item_name"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.db.get_default(\"company\")",
"remote_fieldname": "company_name"
},
{
"is_child_table": 0,
"local_fieldname": "image",
"remote_fieldname": "image"
},
{
"is_child_table": 0,
"local_fieldname": "item_group",
"remote_fieldname": "item_group"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.session.user",
"remote_fieldname": "seller"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.db.get_default(\"country\")",
"remote_fieldname": "country"
}
],
"idx": 1,
"local_doctype": "Item",
"mapping_name": "Item to Hub Item",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2017-10-09 17:30:17.890337",
"modified_by": "Administrator",
"name": "Item to Hub Item",
"owner": "Administrator",
"page_length": 10,
"remote_objectname": "Hub Item",
"remote_primary_key": "item_code"
}

View File

@@ -1,26 +0,0 @@
{
"creation": "2017-09-07 11:39:38.445902",
"docstatus": 0,
"doctype": "Data Migration Plan",
"idx": 1,
"mappings": [
{
"enabled": 1,
"mapping": "Company to Hub Company"
},
{
"enabled": 1,
"mapping": "Item to Hub Item"
},
{
"enabled": 1,
"mapping": "Hub Message to Lead"
}
],
"modified": "2017-10-09 17:30:17.680059",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Sync",
"owner": "Administrator",
"plan_name": "Hub Sync"
}

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,87 +0,0 @@
frappe.ui.form.on("Hub Settings", {
refresh: function(frm) {
frm.add_custom_button(__('Logs'),
() => frappe.set_route('List', 'Data Migration Run', {
data_migration_plan: 'Hub Sync'
}));
frm.trigger("enabled");
if (frm.doc.enabled) {
frm.add_custom_button(__('View Hub'),
() => frappe.set_route('hub'));
frm.add_custom_button(__('Sync'),
() => frm.call('sync'));
}
},
onload: function(frm) {
if(!frm.doc.country) {
frm.set_value("country", frappe.defaults.get_default("Country"));
}
if(!frm.doc.company) {
frm.set_value("company", frappe.defaults.get_default("Company"));
}
},
onload_post_render: function(frm) {
if(frm.get_field("unregister_from_hub").$input)
frm.get_field("unregister_from_hub").$input.addClass("btn-danger");
},
on_update: function(frm) {
},
enabled: function(frm) {
if(!frm.doc.enabled) {
frm.trigger("set_enable_hub_primary_button");
} else {
frm.page.set_primary_action(__("Save Settings"), () => {
frm.save();
});
}
},
hub_user_email: function(frm) {
if(frm.doc.hub_user_email){
frm.set_value("hub_user_name", frappe.user.full_name(frm.doc.hub_user_email));
}
},
set_enable_hub_primary_button: (frm) => {
frm.page.set_primary_action(__("Enable Hub"), () => {
if(frappe.session.user === "Administrator") {
frappe.msgprint("Please login as another user.")
} else {
frappe.verify_password(() => {
this.frm.call({
doc: this.frm.doc,
method: "register",
args: {},
freeze: true,
callback: function(r) {},
onerror: function() {
frappe.msgprint(__("Wrong Password"));
frm.set_value("enabled", 0);
}
});
} );
}
});
},
// update_hub: (frm) => {
// this.frm.call({
// doc: this.frm.doc,
// method: "update_hub",
// args: {},
// freeze: true,
// callback: function(r) { },
// onerror: function() { }
// });
// },
unregister_from_hub: (frm) => {
frappe.verify_password(() => {
var d = frappe.confirm(__('Are you sure you want to unregister?'), () => {
frm.call('unregister');
}, () => {}, __('Confirm Action'));
d.get_primary_btn().addClass("btn-danger");
});
},
});

View File

@@ -1,525 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2015-02-18 00:59:34.560476",
"custom": 0,
"description": "",
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enabled",
"fieldtype": "Check",
"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": "Enabled",
"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": "suspended",
"fieldtype": "Check",
"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": "Suspended",
"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": "user",
"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": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:(!doc.enabled)",
"columns": 0,
"depends_on": "",
"fieldname": "seller_profile_section",
"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": "Company and Seller Profile",
"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": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"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": "country",
"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": "Country",
"length": 0,
"no_copy": 0,
"options": "Country",
"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": "seller_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,
"depends_on": "enabled",
"fieldname": "publish_section",
"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": "Publish",
"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": "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 Items to Hub",
"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": "publish",
"fieldname": "publish_pricing",
"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 Pricing",
"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.publish && doc.publish_pricing)",
"fieldname": "selling_price_list",
"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": "Selling Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"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": "publish",
"fieldname": "publish_availability",
"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 Availability",
"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": "publish",
"fieldname": "last_sync_datetime",
"fieldtype": "Datetime",
"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": "Last Sync On",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "enabled",
"fieldname": "unregister_section",
"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": "Unregister",
"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": "unregister_from_hub",
"fieldtype": "Button",
"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": "Unregister from Hub",
"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": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-21 12:13:50.841646",
"modified_by": "manas@erpnext.com",
"module": "Hub Node",
"name": "Hub Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 0,
"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",
"track_changes": 1,
"track_seen": 0
}

View File

@@ -1,103 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, requests, json
from frappe.model.document import Document
from frappe.utils import add_years, now, get_datetime, get_datetime_str
from frappe import _
from erpnext.utilities.product import get_price, get_qty_in_stock
from six import string_types
hub_url = "https://hubmarket.org"
class HubSetupError(frappe.ValidationError): pass
class HubSettings(Document):
def validate(self):
if self.publish_pricing and not self.selling_price_list:
frappe.throw(_("Please select a Price List to publish pricing"))
def get_hub_url(self):
return hub_url
def sync(self):
"""Create and execute Data Migration Run for Hub Sync plan"""
frappe.has_permission('Hub Settings', throw=True)
doc = frappe.get_doc({
'doctype': 'Data Migration Run',
'data_migration_plan': 'Hub Sync',
'data_migration_connector': 'Hub Connector'
}).insert()
doc.run()
def register(self):
""" Create a User on hub.erpnext.org and return username/password """
data = {
'email': frappe.session.user
}
post_url = hub_url + '/api/method/hub.hub.api.register'
response = requests.post(post_url, data=data)
response.raise_for_status()
message = response.json().get('message')
if message and message.get('password'):
self.user = frappe.session.user
self.create_hub_connector(message)
self.company = frappe.defaults.get_user_default('company')
self.enabled = 1
self.save()
def unregister(self):
""" Disable the User on hub.erpnext.org"""
hub_connector = frappe.get_doc(
'Data Migration Connector', 'Hub Connector')
connection = hub_connector.get_connection()
response_doc = connection.update('User', frappe._dict({'enabled': 0}), hub_connector.username)
if response_doc['enabled'] == 0:
self.enabled = 0
self.save()
def create_hub_connector(self, message):
if frappe.db.exists('Data Migration Connector', 'Hub Connector'):
hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
hub_connector.username = message['email']
hub_connector.password = message['password']
hub_connector.save()
return
frappe.get_doc({
'doctype': 'Data Migration Connector',
'connector_type': 'Frappe',
'connector_name': 'Hub Connector',
'hostname': hub_url,
'username': message['email'],
'password': message['password']
}).insert()
def reset_hub_publishing_settings(last_sync_datetime = ""):
doc = frappe.get_doc("Hub Settings", "Hub Settings")
doc.reset_publishing_settings(last_sync_datetime)
doc.in_callback = 1
doc.save()
def reset_hub_settings(last_sync_datetime = ""):
doc = frappe.get_doc("Hub Settings", "Hub Settings")
doc.reset_publishing_settings(last_sync_datetime)
doc.reset_enable()
doc.in_callback = 1
doc.save()
frappe.msgprint(_("Successfully unregistered."))
@frappe.whitelist()
def sync():
hub_settings = frappe.get_doc('Hub Settings')
hub_settings.sync()

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 Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially('Hub Settings', [
// insert a new Hub Settings
() => frappe.tests.make([
// 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 TestHubSettings(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

@@ -354,7 +354,8 @@ class BOM(WebsiteGenerator):
bom_list = self.traverse_tree(bom_list)
for bom in bom_list:
bom_obj = frappe.get_doc("BOM", bom)
bom_obj.on_update()
bom_obj.check_recursion()
bom_obj.update_exploded_items()
return bom_list

View File

@@ -16,16 +16,23 @@ class BOMUpdateTool(Document):
self.update_new_bom()
bom_list = self.get_parent_boms(self.new_bom)
updated_bom = []
for bom in bom_list:
bom_obj = frappe.get_doc("BOM", bom)
bom_obj.get_doc_before_save()
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
bom_obj.calculate_cost()
bom_obj.update_parent_cost()
bom_obj.db_update()
if (getattr(bom_obj.meta, 'track_changes', False)
and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
bom_obj.save_version()
try:
bom_obj = frappe.get_doc("BOM", bom)
bom_obj.get_doc_before_save()
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
bom_obj.calculate_cost()
bom_obj.update_parent_cost()
bom_obj.db_update()
if (getattr(bom_obj.meta, 'track_changes', False)
and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
bom_obj.save_version()
frappe.db.commit()
except Exception:
frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
def validate_bom(self):
if cstr(self.current_bom) == cstr(self.new_bom):
@@ -55,14 +62,14 @@ class BOMUpdateTool(Document):
bom_list.append(d[0])
self.get_parent_boms(d[0], bom_list)
return bom_list
return list(set(bom_list))
@frappe.whitelist()
def enqueue_replace_bom(args):
if isinstance(args, string_types):
args = json.loads(args)
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args)
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=4000)
frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes."))
@frappe.whitelist()

View File

@@ -318,7 +318,7 @@ class ProductionOrder(Document):
from_time, to_time = self.get_start_end_time(timesheet, d.name)
if date_diff(from_time, original_start_time) > plan_days:
if date_diff(from_time, original_start_time) > cint(plan_days):
frappe.throw(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
break

View File

@@ -11,7 +11,6 @@ Support
Utilities
Shopping Cart
Assets
Hub Node
Portal
Maintenance
Education
@@ -20,4 +19,4 @@ Healthcare
Restaurant
Agriculture
ERPNext Integrations
Non Profit
Non Profit

View File

@@ -502,4 +502,8 @@ erpnext.patches.v10_0.taxes_issue_with_pos
erpnext.patches.v10_0.set_qty_in_transactions_based_on_serial_no_input
erpnext.patches.v10_0.show_leaves_of_all_department_members_in_calendar
erpnext.patches.v10_0.update_status_in_purchase_receipt
erpnext.patches.v10_0.update_address_template_for_india
erpnext.patches.v10_0.update_address_template_for_india
erpnext.patches.v10_0.set_discount_amount
erpnext.patches.v10_0.recalculate_gross_margin_for_project
erpnext.patches.v10_0.delete_hub_documents
erpnext.patches.v10_0.update_user_image_in_employee

View File

@@ -0,0 +1,17 @@
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
for dt, dn in (("Page", "Hub"), ("DocType", "Hub Settings"), ("DocType", "Hub Category")):
frappe.delete_doc(dt, dn, ignore_missing=True)
if frappe.db.exists("DocType", "Data Migration Plan"):
data_migration_plans = frappe.get_all("Data Migration Plan", filters={"module": 'Hub Node'})
for plan in data_migration_plans:
plan_doc = frappe.get_doc("Data Migration Plan", plan.name)
for m in plan_doc.get("mappings"):
frappe.delete_doc("Data Migration Mapping", m.mapping, force=True)
frappe.delete_doc("Data Migration Plan", plan.name)
frappe.delete_doc("Module Def", "Hub Node", ignore_missing=True)

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('projects', 'doctype', 'project')
for d in frappe.db.sql(""" select name from `tabProject` where
ifnull(total_consumed_material_cost, 0 ) > 0 and ifnull(total_billed_amount, 0) > 0""", as_dict=1):
doc = frappe.get_doc("Project", d.name)
doc.calculate_gross_margin()
doc.db_set('gross_margin', doc.gross_margin)
doc.db_set('per_gross_margin', doc.per_gross_margin)

View File

@@ -0,0 +1,34 @@
import frappe
def execute():
frappe.reload_doc("accounts", "doctype", "sales_invoice_item")
frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_item')
frappe.reload_doc('buying', 'doctype', 'purchase_order_item')
frappe.reload_doc('buying', 'doctype', 'supplier_quotation_item')
frappe.reload_doc('selling', 'doctype', 'sales_order_item')
frappe.reload_doc('selling', 'doctype', 'quotation_item')
frappe.reload_doc('stock', 'doctype', 'delivery_note_item')
frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
selling_doctypes = ["Sales Order Item", "Sales Invoice Item", "Delivery Note Item", "Quotation Item"]
buying_doctypes = ["Purchase Order Item", "Purchase Invoice Item", "Purchase Receipt Item", "Supplier Quotation Item"]
for doctype in selling_doctypes:
frappe.db.sql('''
UPDATE
`tab%s`
SET
discount_amount = if(rate_with_margin > 0, rate_with_margin, price_list_rate) * discount_percentage / 100
WHERE
discount_percentage > 0
''' % (doctype))
for doctype in buying_doctypes:
frappe.db.sql('''
UPDATE
`tab%s`
SET
discount_amount = price_list_rate * discount_percentage / 100
WHERE
discount_percentage > 0
''' % (doctype))

View File

@@ -7,7 +7,6 @@ from erpnext.setup.doctype.company.company import install_country_fixtures
def execute():
frappe.reload_doc("accounts", "doctype", "account")
frappe.reload_doc("hub_node", "doctype", "hub_category")
frappe.reload_doc("accounts", "doctype", "payment_schedule")
for d in frappe.get_all('Company',
filters={'country': ('in', ['Saudi Arabia', 'United Arab Emirates'])}):

View File

@@ -0,0 +1,19 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('hr', 'doctype', 'employee')
frappe.db.sql("""
UPDATE
`tabEmployee`, `tabUser`
SET
`tabEmployee`.image = `tabUser`.user_image
WHERE
`tabEmployee`.user_id = `tabUser`.name and
`tabEmployee`.user_id is not null and
`tabEmployee`.user_id != '' and `tabEmployee`.image is null
""")

View File

@@ -4,7 +4,6 @@ from frappe.email import sendmail_to_system_managers
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
frappe.reload_doc("stock", "doctype", "customs_tariff_number")
frappe.reload_doc("hub_node", "doctype", "hub_category")
frappe.reload_doc("accounts", "doctype", "payment_terms_template")
frappe.reload_doc("accounts", "doctype", "payment_schedule")

View File

@@ -14,6 +14,7 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -41,10 +42,43 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -71,10 +105,75 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "show_availability_status",
"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": "Show Availability Status",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"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,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -103,6 +202,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@@ -116,7 +216,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-07 19:34:33.055048",
"modified": "2018-08-14 17:59:58.473100",
"modified_by": "Administrator",
"module": "Portal",
"name": "Products Settings",

View File

@@ -52,7 +52,14 @@ class Project(Document):
if self.name is None:
return {}
else:
return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
filters = {"project": self.name}
if self.get("deleted_task_list"):
filters.update({
'name': ("not in", self.deleted_task_list)
})
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc")
def validate(self):
self.validate_project_name()
@@ -62,6 +69,7 @@ class Project(Document):
self.tasks = []
self.load_tasks()
self.send_welcome_email()
self.update_percent_complete()
def validate_project_name(self):
if self.get("__islocal") and frappe.db.exists("Project", self.project_name):
@@ -82,12 +90,26 @@ class Project(Document):
def sync_tasks(self):
"""sync tasks and remove table"""
if not hasattr(self, "deleted_task_list"):
self.set("deleted_task_list", [])
if self.flags.dont_sync_tasks: return
task_names = []
existing_task_data = {}
fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"]
exclude_fieldtype = ["Button", "Column Break",
"Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"]
custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task",
"fieldtype": ("not in", exclude_fieldtype)}, "fieldname")
for d in custom_fields:
fields.append(d.fieldname)
for d in frappe.get_all('Project Task',
fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"],
fields = fields,
filters = {'parent': self.name}):
existing_task_data.setdefault(d.task_id, d)
@@ -98,7 +120,7 @@ class Project(Document):
task = frappe.new_doc("Task")
task.project = self.name
if not t.task_id or self.is_row_updated(t, existing_task_data):
if not t.task_id or self.is_row_updated(t, existing_task_data, fields):
task.update({
"subject": t.title,
"status": t.status,
@@ -120,7 +142,7 @@ class Project(Document):
"modified": now()
})
task.validate()
task.run_method("validate")
task.db_update()
else:
task.save(ignore_permissions = True)
@@ -130,21 +152,20 @@ class Project(Document):
# delete
for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}):
frappe.delete_doc("Task", t.name)
self.deleted_task_list.append(t.name)
def update_costing_and_percentage_complete(self):
self.update_percent_complete()
self.update_costing()
def is_row_updated(self, row, existing_task_data):
def is_row_updated(self, row, existing_task_data, fields):
if self.get("__islocal") or not existing_task_data: return True
d = existing_task_data.get(row.task_id)
if (d and (row.title != d.title or row.status != d.status
or getdate(row.start_date) != getdate(d.start_date) or getdate(row.end_date) != getdate(d.end_date)
or row.description != d.description or row.task_weight != d.task_weight)):
return True
for field in fields:
if row.get(field) != d.get(field):
return True
def map_custom_fields(self, source, target):
project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname")
@@ -216,9 +237,13 @@ class Project(Document):
self.update_purchase_costing()
self.update_sales_amount()
self.update_billed_amount()
self.calculate_gross_margin()
self.gross_margin = flt(self.total_billed_amount) - (flt(self.total_costing_amount) + flt(self.total_expense_claim) + flt(self.total_purchase_cost))
def calculate_gross_margin(self):
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
+ flt(self.total_purchase_cost) + flt(self.get('total_consumed_material_cost', 0)))
self.gross_margin = flt(self.total_billed_amount) - expense_amount
if self.total_billed_amount:
self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) *100
@@ -263,9 +288,19 @@ class Project(Document):
user.welcome_email_sent=1
def on_update(self):
self.delete_task()
self.load_tasks()
self.update_costing_and_percentage_complete()
self.update_dependencies_on_duplicated_project()
def delete_task(self):
if not self.get('deleted_task_list'): return
for d in self.get('deleted_task_list'):
frappe.delete_doc("Task", d)
self.deleted_task_list = []
def update_dependencies_on_duplicated_project(self):
if self.flags.dont_sync_tasks: return
if not self.copied_from:

View File

@@ -26,6 +26,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
};
});
if (this.frm.doc.__islocal
&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
this.frm.set_value("disable_rounded_total", cint(frappe.sys_defaults.disable_rounded_total));
}
/* eslint-disable */
// no idea where me is coming from
if(this.frm.get_field('shipping_address')) {
@@ -108,8 +113,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0),
precision("rate", item));
item.discount_amount = flt(item.price_list_rate) * flt(item.discount_percentage) / 100;
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
this.calculate_taxes_and_totals();
},
@@ -222,6 +227,69 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
tc_name: function() {
this.get_terms();
},
link_to_mrs: function() {
var my_items = [];
for (var i in cur_frm.doc.items) {
if(!cur_frm.doc.items[i].material_request){
my_items.push(cur_frm.doc.items[i].item_code);
}
}
frappe.call({
method: "erpnext.buying.utils.get_linked_material_requests",
args:{
items: my_items
},
callback: function(r) {
if(!r.message) {
frappe.throw(__("No pending Material Requests found to link for the given items."))
}
else {
var i = 0;
var item_length = cur_frm.doc.items.length;
while (i < item_length) {
var qty = cur_frm.doc.items[i].qty;
(r.message[0] || []).forEach(function(d) {
if (d.qty > 0 && qty > 0 && cur_frm.doc.items[i].item_code == d.item_code && !cur_frm.doc.items[i].material_request_item)
{
cur_frm.doc.items[i].material_request = d.mr_name;
cur_frm.doc.items[i].material_request_item = d.mr_item;
var my_qty = Math.min(qty, d.qty);
qty = qty - my_qty;
d.qty = d.qty - my_qty;
cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor;
cur_frm.doc.items[i].qty = my_qty;
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
if (qty > 0)
{
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items");
item_length++;
for (var key in cur_frm.doc.items[i])
{
newrow[key] = cur_frm.doc.items[i][key];
}
newrow.idx = item_length;
newrow["stock_qty"] = newrow.conversion_factor*qty;
newrow["qty"] = qty;
newrow["material_request"] = "";
newrow["material_request_item"] = "";
}
}
});
i++;
}
refresh_field("items");
//cur_frm.save();
}
}
});
}
});

View File

@@ -16,8 +16,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
item.rate = flt(item.rate_with_margin , precision("rate", item));
if(item.discount_percentage){
var discount_value = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
item.rate = flt((item.rate_with_margin) - (discount_value), precision('rate', item));
item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
item.rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
}
},

View File

@@ -138,10 +138,15 @@ erpnext.setup.slides_settings = [
validate: function () {
// validate fiscal year start and end dates
if (this.values.fy_start_date == 'Invalid date' || this.values.fy_end_date == 'Invalid date') {
const invalid = this.values.fy_start_date == 'Invalid date' ||
this.values.fy_end_date == 'Invalid date';
const start_greater_than_end = this.values.fy_start_date > this.values.fy_end_date;
if (invalid || start_greater_than_end) {
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
return false;
}
return true;
},

View File

@@ -790,6 +790,38 @@
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -1766,7 +1798,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-12-14 09:39:17.180709",
"modified": "2018-08-06 05:18:38.135668",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",

View File

@@ -820,6 +820,38 @@
"unique": 0,
"width": "70px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2146,7 +2178,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-11-30 14:05:39.173834",
"modified": "2018-08-06 05:17:51.297862",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import msgprint, _
from frappe.utils import flt
from erpnext import get_company_currency
def execute(filters=None):
if not filters: filters = {}
@@ -14,12 +15,14 @@ def execute(filters=None):
item_details = get_item_details()
data = []
company_currency = get_company_currency(filters["company"])
for d in entries:
if d.stock_qty > 0 or filters.get('show_return_entries', 0):
data.append([
d.name, d.customer, d.territory, d.posting_date, d.item_code,
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt
d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt, company_currency
])
if data:
@@ -32,12 +35,105 @@ def get_columns(filters):
if not filters.get("doc_type"):
msgprint(_("Please select the document type first"), raise_exception=1)
return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140",
_("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Posting Date") + ":Date:100",
_("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120",
_("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120",
_("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110",
_("Contribution Amount") + ":Currency:140"]
columns = [
{
"label": _(filters["doc_type"]),
"options": filters["doc_type"],
"fieldname": frappe.scrub(filters['doc_type']),
"fieldtype": "Link",
"width": 140
},
{
"label": _("Customer"),
"options": "Customer",
"fieldname": "customer",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Territory"),
"options": "Territory",
"fieldname": "territory",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Warehouse"),
"options": "Warehouse",
"fieldname": "warehouse",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Posting Date"),
"fieldname": "posting_date",
"fieldtype": "Date",
"width": 140
},
{
"label": _("Item Code"),
"options": "Item",
"fieldname": "item_code",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Item Group"),
"options": "Item Group",
"fieldname": "item_group",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Brand"),
"options": "Brand",
"fieldname": "brand",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Qty"),
"fieldname": "qty",
"fieldtype": "Float",
"width": 140
},
{
"label": _("Amount"),
"options": "currency",
"fieldname": "amount",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Sales Person"),
"options": "Sales Person",
"fieldname": "sales_person",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Contribution %"),
"fieldname": "contribution",
"fieldtype": "Float",
"width": 140
},
{
"label": _("Contribution Amount"),
"options": "currency",
"fieldname": "contribution_amt",
"fieldtype": "Currency",
"width": 140
},
{
"label":_("Currency"),
"options": "Currency",
"fieldname":"currency",
"fieldtype":"Link",
"hidden" : 1
}
]
return columns
def get_entries(filters):
date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date"

View File

@@ -138,6 +138,9 @@ def get_item_for_list_in_html(context):
if (context.get("website_image") or "").startswith("files/"):
context["website_image"] = "/" + urllib.quote(context["website_image"])
context["show_availability_status"] = cint(frappe.db.get_single_value('Products Settings',
'show_availability_status'))
products_template = 'templates/includes/products_as_grid.html'
if cint(frappe.db.get_single_value('Products Settings', 'products_as_list')):
products_template = 'templates/includes/products_as_list.html'

View File

@@ -30,7 +30,7 @@ def make_tax_account_and_template(company, account_name, tax_rate, template_name
if accounts:
make_sales_and_purchase_tax_templates(accounts, template_name)
except frappe.NameError:
frappe.message_log.pop()
if frappe.message_log: frappe.message_log.pop()
except RootNotEditable:
pass
@@ -50,7 +50,7 @@ def make_tax_account(company, account_name, tax_rate):
"tax_rate": flt(tax_rate) if tax_rate else None
}).insert(ignore_permissions=True, ignore_mandatory=True)
except frappe.NameError:
frappe.message_log.pop()
if frappe.message_log: frappe.message_log.pop()
abbr = frappe.db.get_value('Company', company, 'abbr')
account = '{0} - {1}'.format(account_name, abbr)
return frappe.get_doc('Account', account)

View File

@@ -255,13 +255,13 @@ erpnext.stock.delivery_note.set_print_hide = function(doc, cdt, cdn){
var dn_item_fields = frappe.meta.docfield_map['Delivery Note Item'];
var dn_fields_copy = dn_fields;
var dn_item_fields_copy = dn_item_fields;
if (doc.print_without_amount) {
dn_fields['currency'].print_hide = 1;
dn_item_fields['rate'].print_hide = 1;
dn_item_fields['discount_percentage'].print_hide = 1;
dn_item_fields['price_list_rate'].print_hide = 1;
dn_item_fields['amount'].print_hide = 1;
dn_item_fields['discount_amount'].print_hide = 1;
dn_fields['taxes'].print_hide = 1;
} else {
if (dn_fields_copy['currency'].print_hide != 1)
@@ -270,6 +270,8 @@ erpnext.stock.delivery_note.set_print_hide = function(doc, cdt, cdn){
dn_item_fields['rate'].print_hide = 0;
if (dn_item_fields_copy['amount'].print_hide != 1)
dn_item_fields['amount'].print_hide = 0;
if (dn_item_fields_copy['discount_amount'].print_hide != 1)
dn_item_fields['discount_amount'].print_hide = 0;
if (dn_fields_copy['taxes'].print_hide != 1)
dn_fields['taxes'].print_hide = 0;
}

View File

@@ -75,7 +75,7 @@ class DeliveryNote(SellingController):
item_meta = frappe.get_meta("Delivery Note Item")
print_hide_fields = {
"parent": ["grand_total", "rounded_total", "in_words", "currency", "total", "taxes"],
"items": ["rate", "amount", "price_list_rate", "discount_percentage"]
"items": ["rate", "amount", "discount_amount", "price_list_rate", "discount_percentage"]
}
for key, fieldname in print_hide_fields.items():

View File

@@ -819,6 +819,38 @@
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2139,7 +2171,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-11-30 14:07:12.217563",
"modified": "2018-08-06 05:18:26.132899",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Item",

View File

@@ -3451,38 +3451,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hub_category_to_publish",
"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": "Hub Category to Publish",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -3561,7 +3529,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2018-04-11 12:21:48.715529",
"modified": "2018-09-06 14:45:48.715529",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -6,7 +6,8 @@ def get_data():
'non_standard_fieldnames': {
'Purchase Invoice': 'purchase_receipt',
'Landed Cost Voucher': 'receipt_document',
'Subscription': 'reference_document'
'Subscription': 'reference_document',
'Purchase Receipt': 'return_against'
},
'internal_links': {
'Purchase Order': ['items', 'purchase_order'],
@@ -24,7 +25,7 @@ def get_data():
},
{
'label': _('Returns'),
'items': ['Stock Entry']
'items': ['Purchase Receipt']
},
{
'label': _('Subscription'),

View File

@@ -759,6 +759,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "discount_percentage",
"fieldname": "discount_amount",
"fieldtype": "Currency",
"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": "Discount Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2290,7 +2322,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-06 13:50:08.201145",
"modified": "2018-08-06 05:16:48.590787",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",

View File

@@ -187,13 +187,14 @@ def process_serial_no(sle):
update_serial_nos(sle, item_det)
def validate_serial_no(sle, item_det):
serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else []
if item_det.has_serial_no==0:
if sle.serial_no:
if serial_nos:
frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
SerialNoNotRequiredError)
elif sle.is_cancelled == "No":
if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no)
if serial_nos:
if cint(sle.actual_qty) != flt(sle.actual_qty):
frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
@@ -239,6 +240,12 @@ def validate_serial_no(sle, item_det):
elif sle.actual_qty < 0 or not item_det.serial_no_series:
frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
SerialNoRequiredError)
elif serial_nos:
for serial_no in serial_nos:
sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse"], as_dict=1)
if sr and sle.actual_qty < 0 and sr.warehouse != sle.warehouse:
frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
.format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
def has_duplicate_serial_no(sn, sle):
if sn.warehouse:

View File

@@ -82,6 +82,7 @@ class StockEntry(StockController):
if self.purchase_order and self.purpose == "Subcontract":
self.update_purchase_order_supplied_items()
self.make_gl_entries_on_cancel()
self.update_cost_in_project()
def validate_purpose(self):
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", "Material Transfer for Manufacture",
@@ -107,8 +108,18 @@ class StockEntry(StockController):
se.docstatus = 1 and se.project = %s and sed.parent = se.name
and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1)
if amount:
frappe.db.set_value('Project', self.project, 'total_consumed_material_cost', amount[0][0])
amount = amount[0][0] if amount else 0
additional_costs = frappe.db.sql(""" select ifnull(sum(sed.amount), 0)
from
`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
where
se.docstatus = 1 and se.project = %s and sed.parent = se.name
and se.purpose = 'Manufacture'""", self.project, as_list=1)
additional_cost_amt = additional_costs[0][0] if additional_costs else 0
amount += additional_cost_amt
frappe.db.set_value('Project', self.project, 'total_consumed_material_cost', amount)
def validate_item(self):
stock_items = self.get_stock_items()

View File

@@ -109,7 +109,7 @@ class StockReconciliation(StockController):
self.validation_messages.append(_get_msg(row_num,
_("Negative Valuation Rate is not allowed")))
if row.qty and not row.valuation_rate:
if row.qty and row.valuation_rate in ["", None]:
row.valuation_rate = get_stock_balance(row.item_code, row.warehouse,
self.posting_date, self.posting_time, with_valuation_rate=True)[1]
if not row.valuation_rate:

Some files were not shown because too many files have changed in this diff Show More