Merge branch 'frappe:develop' into currency-exchange-settings

This commit is contained in:
Dany Robert 2021-11-03 18:15:27 +05:30 committed by GitHub
commit d869b3933b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
146 changed files with 1534 additions and 5736 deletions

View File

@ -11,4 +11,4 @@ jobs:
- name: curl
run: |
apk add curl bash
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}'

View File

@ -86,4 +86,27 @@ jobs:
cd ~/frappe-bench/
wget https://erpnext.com/files/v10-erpnext.sql.gz
bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz
git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
for version in $(seq 12 13)
do
echo "Updating to v$version"
branch_name="version-$version"
git -C "apps/frappe" fetch --depth 1 upstream $branch_name:$branch_name
git -C "apps/erpnext" fetch --depth 1 upstream $branch_name:$branch_name
git -C "apps/frappe" checkout -q -f $branch_name
git -C "apps/erpnext" checkout -q -f $branch_name
bench setup requirements --python
bench --site test_site migrate
done
echo "Updating to latest version"
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
bench --site test_site migrate

View File

@ -81,7 +81,7 @@ def add_suffix_if_duplicate(account_name, account_number, accounts):
def identify_is_group(child):
if child.get("is_group"):
is_group = child.get("is_group")
elif len(set(child.keys()) - set(["account_type", "root_type", "is_group", "tax_rate", "account_number"])):
elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])):
is_group = 1
else:
is_group = 0

View File

@ -58,7 +58,8 @@ class GLEntry(Document):
# Update outstanding amt on against voucher
if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
and self.against_voucher and self.flags.update_outstanding == 'Yes'):
and self.against_voucher and self.flags.update_outstanding == 'Yes'
and not frappe.flags.is_reverse_depr_entry):
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher)

View File

@ -58,7 +58,10 @@ class JournalEntry(AccountsController):
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
self.validate_against_jv()
if not frappe.flags.is_reverse_depr_entry:
self.validate_against_jv()
self.validate_stock_accounts()
self.validate_reference_doc()
if self.docstatus == 0:
self.set_against_account()
@ -69,7 +72,6 @@ class JournalEntry(AccountsController):
self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.validate_inter_company_accounts()
self.validate_stock_accounts()
if self.docstatus == 0:
self.apply_tax_withholding()

View File

@ -389,7 +389,7 @@ class PaymentEntry(AccountsController):
invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
for key, allocated_amount in iteritems(invoice_payment_amount_map):
for idx, (key, allocated_amount) in enumerate(iteritems(invoice_payment_amount_map), 1):
if not invoice_paid_amount_map.get(key):
frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1]))
@ -407,7 +407,7 @@ class PaymentEntry(AccountsController):
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
else:
if allocated_amount > outstanding:
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
frappe.throw(_('Row #{0}: Cannot allocate more than {1} against payment term {2}').format(idx, outstanding, key[0]))
if allocated_amount and outstanding:
frappe.db.sql("""
@ -1053,12 +1053,6 @@ def get_outstanding_reference_documents(args):
party_account_currency = get_account_currency(args.get("party_account"))
company_currency = frappe.get_cached_value('Company', args.get("company"), "default_currency")
# Get negative outstanding sales /purchase invoices
negative_outstanding_invoices = []
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
args.get("party_account"), args.get("company"), party_account_currency, company_currency)
# Get positive outstanding sales /purchase invoices/ Fees
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
@ -1105,6 +1099,12 @@ def get_outstanding_reference_documents(args):
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
# Get negative outstanding sales /purchase invoices
negative_outstanding_invoices = []
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
args.get("party_account"), party_account_currency, company_currency, condition=condition)
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
if not data:
@ -1137,22 +1137,26 @@ def split_invoices_based_on_payment_terms(outstanding_invoices):
'invoice_amount': flt(d.invoice_amount),
'outstanding_amount': flt(d.outstanding_amount),
'payment_amount': payment_term.payment_amount,
'payment_term': payment_term.payment_term,
'allocated_amount': payment_term.outstanding
'payment_term': payment_term.payment_term
}))
outstanding_invoices_after_split = []
if invoice_ref_based_on_payment_terms:
for idx, ref in invoice_ref_based_on_payment_terms.items():
voucher_no = outstanding_invoices[idx]['voucher_no']
voucher_type = outstanding_invoices[idx]['voucher_type']
voucher_no = ref[0]['voucher_no']
voucher_type = ref[0]['voucher_type']
frappe.msgprint(_("Spliting {} {} into {} rows as per payment terms").format(
frappe.msgprint(_("Spliting {} {} into {} row(s) as per Payment Terms").format(
voucher_type, voucher_no, len(ref)), alert=True)
outstanding_invoices.pop(idx - 1)
outstanding_invoices += invoice_ref_based_on_payment_terms[idx]
outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
return outstanding_invoices
existing_row = list(filter(lambda x: x.get('voucher_no') == voucher_no, outstanding_invoices))
index = outstanding_invoices.index(existing_row[0])
outstanding_invoices.pop(index)
outstanding_invoices_after_split += outstanding_invoices
return outstanding_invoices_after_split
def get_orders_to_be_billed(posting_date, party_type, party,
company, party_account_currency, company_currency, cost_center=None, filters=None):
@ -1219,7 +1223,7 @@ def get_orders_to_be_billed(posting_date, party_type, party,
return order_list
def get_negative_outstanding_invoices(party_type, party, party_account,
company, party_account_currency, company_currency, cost_center=None):
party_account_currency, company_currency, cost_center=None, condition=None):
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@ -1241,19 +1245,21 @@ def get_negative_outstanding_invoices(party_type, party, party_account,
`tab{voucher_type}`
where
{party_type} = %s and {party_account} = %s and docstatus = 1 and
company = %s and outstanding_amount < 0
outstanding_amount < 0
{supplier_condition}
{condition}
order by
posting_date, name
""".format(**{
"supplier_condition": supplier_condition,
"condition": condition,
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
"cost_center": cost_center
}), (party, party_account, company), as_dict=True)
}), (party, party_account), as_dict=True)
@frappe.whitelist()

View File

@ -10,6 +10,9 @@ frappe.ui.form.on('Payment Order', {
}
}
});
frm.set_df_property('references', 'cannot_add_rows', true);
frm.set_df_property('references', 'cannot_delete_rows', true);
},
refresh: function(frm) {
if (frm.doc.docstatus == 0) {

View File

@ -4,9 +4,14 @@
frappe.provide("erpnext.accounts");
erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller {
onload() {
var me = this;
const default_company = frappe.defaults.get_default('company');
this.frm.set_value('company', default_company);
this.frm.set_query("party_type", function() {
this.frm.set_value('party_type', '');
this.frm.set_value('party', '');
this.frm.set_value('receivable_payable_account', '');
this.frm.set_query("party_type", () => {
return {
"filters": {
"name": ["in", Object.keys(frappe.boot.party_account_types)],
@ -14,44 +19,30 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
});
this.frm.set_query('receivable_payable_account', function() {
check_mandatory(me.frm);
this.frm.set_query('receivable_payable_account', () => {
return {
filters: {
"company": me.frm.doc.company,
"company": this.frm.doc.company,
"is_group": 0,
"account_type": frappe.boot.party_account_types[me.frm.doc.party_type]
"account_type": frappe.boot.party_account_types[this.frm.doc.party_type]
}
};
});
this.frm.set_query('bank_cash_account', function() {
check_mandatory(me.frm, true);
this.frm.set_query('bank_cash_account', () => {
return {
filters:[
['Account', 'company', '=', me.frm.doc.company],
['Account', 'company', '=', this.frm.doc.company],
['Account', 'is_group', '=', 0],
['Account', 'account_type', 'in', ['Bank', 'Cash']]
]
};
});
this.frm.set_value('party_type', '');
this.frm.set_value('party', '');
this.frm.set_value('receivable_payable_account', '');
var check_mandatory = (frm, only_company=false) => {
var title = __("Mandatory");
if (only_company && !frm.doc.company) {
frappe.throw({message: __("Please Select a Company First"), title: title});
} else if (!frm.doc.company || !frm.doc.party_type) {
frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title});
}
};
}
refresh() {
this.frm.disable_save();
this.frm.set_df_property('invoices', 'cannot_delete_rows', true);
this.frm.set_df_property('payments', 'cannot_delete_rows', true);
this.frm.set_df_property('allocation', 'cannot_delete_rows', true);
@ -85,76 +76,92 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
company() {
var me = this;
this.frm.set_value('party', '');
this.frm.set_value('receivable_payable_account', '');
me.frm.clear_table("allocation");
me.frm.clear_table("invoices");
me.frm.clear_table("payments");
me.frm.refresh_fields();
me.frm.trigger('party');
}
party_type() {
this.frm.set_value('party', '');
}
party() {
var me = this;
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
this.frm.set_value('receivable_payable_account', '');
this.frm.trigger("clear_child_tables");
if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) {
return frappe.call({
method: "erpnext.accounts.party.get_party_account",
args: {
company: me.frm.doc.company,
party_type: me.frm.doc.party_type,
party: me.frm.doc.party
company: this.frm.doc.company,
party_type: this.frm.doc.party_type,
party: this.frm.doc.party
},
callback: function(r) {
callback: (r) => {
if (!r.exc && r.message) {
me.frm.set_value("receivable_payable_account", r.message);
this.frm.set_value("receivable_payable_account", r.message);
}
me.frm.refresh();
this.frm.refresh();
}
});
}
}
receivable_payable_account() {
this.frm.trigger("clear_child_tables");
this.frm.refresh();
}
clear_child_tables() {
this.frm.clear_table("invoices");
this.frm.clear_table("payments");
this.frm.clear_table("allocation");
this.frm.refresh_fields();
}
get_unreconciled_entries() {
var me = this;
this.frm.clear_table("allocation");
return this.frm.call({
doc: me.frm.doc,
doc: this.frm.doc,
method: 'get_unreconciled_entries',
callback: function(r, rt) {
if (!(me.frm.doc.payments.length || me.frm.doc.invoices.length)) {
frappe.throw({message: __("No invoice and payment records found for this party")});
callback: () => {
if (!(this.frm.doc.payments.length || this.frm.doc.invoices.length)) {
frappe.throw({message: __("No Unreconciled Invoices and Payments found for this party and account")});
} else if (!(this.frm.doc.invoices.length)) {
frappe.throw({message: __("No Outstanding Invoices found for this party")});
} else if (!(this.frm.doc.payments.length)) {
frappe.throw({message: __("No Unreconciled Payments found for this party")});
}
me.frm.refresh();
this.frm.refresh();
}
});
}
allocate() {
var me = this;
let payments = me.frm.fields_dict.payments.grid.get_selected_children();
let payments = this.frm.fields_dict.payments.grid.get_selected_children();
if (!(payments.length)) {
payments = me.frm.doc.payments;
payments = this.frm.doc.payments;
}
let invoices = me.frm.fields_dict.invoices.grid.get_selected_children();
let invoices = this.frm.fields_dict.invoices.grid.get_selected_children();
if (!(invoices.length)) {
invoices = me.frm.doc.invoices;
invoices = this.frm.doc.invoices;
}
return me.frm.call({
doc: me.frm.doc,
return this.frm.call({
doc: this.frm.doc,
method: 'allocate_entries',
args: {
payments: payments,
invoices: invoices
},
callback: function() {
me.frm.refresh();
callback: () => {
this.frm.refresh();
}
});
}
reconcile() {
var me = this;
var show_dialog = me.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
if (show_dialog && show_dialog.length) {
@ -186,10 +193,10 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
label: __("Difference Account"),
fieldname: 'difference_account',
reqd: 1,
get_query: function() {
get_query: () => {
return {
filters: {
company: me.frm.doc.company,
company: this.frm.doc.company,
is_group: 0
}
}
@ -203,7 +210,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}]
},
],
primary_action: function() {
primary_action: () => {
const args = dialog.get_values()["allocation"];
args.forEach(d => {
@ -211,7 +218,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
"difference_account", d.difference_account);
});
me.reconcile_payment_entries();
this.reconcile_payment_entries();
dialog.hide();
},
primary_action_label: __('Reconcile Entries')
@ -237,15 +244,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
reconcile_payment_entries() {
var me = this;
return this.frm.call({
doc: me.frm.doc,
doc: this.frm.doc,
method: 'reconcile',
callback: function(r, rt) {
me.frm.clear_table("allocation");
me.frm.refresh_fields();
me.frm.refresh();
callback: () => {
this.frm.clear_table("allocation");
this.frm.refresh();
}
});
}

View File

@ -114,6 +114,8 @@ class POSInvoiceMergeLog(Document):
def merge_pos_invoice_into(self, invoice, data):
items, payments, taxes = [], [], []
loyalty_amount_sum, loyalty_points_sum = 0, 0
rounding_adjustment, base_rounding_adjustment = 0, 0
rounded_total, base_rounded_total = 0, 0
for doc in data:
map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
@ -162,6 +164,11 @@ class POSInvoiceMergeLog(Document):
found = True
if not found:
payments.append(payment)
rounding_adjustment += doc.rounding_adjustment
rounded_total += doc.rounded_total
base_rounding_adjustment += doc.rounding_adjustment
base_rounded_total += doc.rounded_total
if loyalty_points_sum:
invoice.redeem_loyalty_points = 1
@ -171,6 +178,10 @@ class POSInvoiceMergeLog(Document):
invoice.set('items', items)
invoice.set('payments', payments)
invoice.set('taxes', taxes)
invoice.set('rounding_adjustment',rounding_adjustment)
invoice.set('rounding_adjustment',base_rounding_adjustment)
invoice.set('base_rounded_total',base_rounded_total)
invoice.set('rounded_total',rounded_total)
invoice.additional_discount_percentage = 0
invoice.discount_amount = 0.0
invoice.taxes_and_charges = None

View File

@ -2,11 +2,11 @@
// For license information, please see license.txt
let search_fields_datatypes = ['Data', 'Link', 'Dynamic Link', 'Long Text', 'Select', 'Small Text', 'Text', 'Text Editor'];
let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "hub_sync_id", "asset_naming_series",
let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "asset_naming_series",
"default_material_request_type", "valuation_method", "warranty_period", "weight_uom", "batch_number_series",
"serial_no_series", "purchase_uom", "customs_tariff_number", "sales_uom", "deferred_revenue_account",
"deferred_expense_account", "quality_inspection_template", "route", "slideshow", "website_image_alt", "thumbnail",
"web_long_description", "hub_sync_id"]
"web_long_description"]
frappe.ui.form.on('POS Settings', {
onload: function(frm) {

View File

@ -29,6 +29,9 @@ def get_pricing_rules(args, doc=None):
pricing_rules = []
values = {}
if not frappe.db.exists('Pricing Rule', {'disable': 0, args.transaction_type: 1}):
return
for apply_on in ['Item Code', 'Item Group', 'Brand']:
pricing_rules.extend(_get_pricing_rules(apply_on, args, values))
if pricing_rules and not apply_multiple_pricing_rules(pricing_rules):

View File

@ -37,7 +37,7 @@ from erpnext.assets.doctype.asset.depreciation import (
get_disposal_account_and_cost_center,
get_gl_entries_on_asset_disposal,
get_gl_entries_on_asset_regain,
post_depreciation_entries,
make_depreciation_entry,
)
from erpnext.controllers.selling_controller import SellingController
from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
@ -934,6 +934,7 @@ class SalesInvoice(SellingController):
asset.db_set("disposal_date", None)
if asset.calculate_depreciation:
self.reverse_depreciation_entry_made_after_sale(asset)
self.reset_depreciation_schedule(asset)
else:
@ -997,22 +998,20 @@ class SalesInvoice(SellingController):
def depreciate_asset(self, asset):
asset.flags.ignore_validate_update_after_submit = True
asset.prepare_depreciation_data(self.posting_date)
asset.prepare_depreciation_data(date_of_sale=self.posting_date)
asset.save()
post_depreciation_entries(self.posting_date)
make_depreciation_entry(asset.name, self.posting_date)
def reset_depreciation_schedule(self, asset):
asset.flags.ignore_validate_update_after_submit = True
# recreate original depreciation schedule of the asset
asset.prepare_depreciation_data()
asset.prepare_depreciation_data(date_of_return=self.posting_date)
self.modify_depreciation_schedule_for_asset_repairs(asset)
asset.save()
self.delete_depreciation_entry_made_after_sale(asset)
def modify_depreciation_schedule_for_asset_repairs(self, asset):
asset_repairs = frappe.get_all(
'Asset Repair',
@ -1026,7 +1025,7 @@ class SalesInvoice(SellingController):
asset_repair.modify_depreciation_schedule()
asset.prepare_depreciation_data()
def delete_depreciation_entry_made_after_sale(self, asset):
def reverse_depreciation_entry_made_after_sale(self, asset):
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
@ -1041,11 +1040,19 @@ class SalesInvoice(SellingController):
row += 1
if schedule.schedule_date == posting_date_of_original_invoice:
if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice):
if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \
or self.sale_happens_in_the_future(posting_date_of_original_invoice):
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
reverse_journal_entry.posting_date = nowdate()
frappe.flags.is_reverse_depr_entry = True
reverse_journal_entry.submit()
frappe.flags.is_reverse_depr_entry = False
asset.flags.ignore_validate_update_after_submit = True
schedule.journal_entry = None
asset.save()
def get_posting_date_of_sales_invoice(self):
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
@ -1060,6 +1067,12 @@ class SalesInvoice(SellingController):
return True
return False
def sale_happens_in_the_future(self, posting_date_of_original_invoice):
if posting_date_of_original_invoice > getdate():
return True
return False
@property
def enable_discount_accounting(self):
if not hasattr(self, "_enable_discount_accounting"):
@ -1975,22 +1988,23 @@ def update_multi_mode_option(doc, pos_profile):
def append_payment(payment_mode):
payment = doc.append('payments', {})
payment.default = payment_mode.default
payment.mode_of_payment = payment_mode.parent
payment.mode_of_payment = payment_mode.mop
payment.account = payment_mode.default_account
payment.type = payment_mode.type
doc.set('payments', [])
invalid_modes = []
for pos_payment_method in pos_profile.get('payments'):
pos_payment_method = pos_payment_method.as_dict()
mode_of_payments = [d.mode_of_payment for d in pos_profile.get('payments')]
mode_of_payments_info = get_mode_of_payments_info(mode_of_payments, doc.company)
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
for row in pos_profile.get('payments'):
payment_mode = mode_of_payments_info.get(row.mode_of_payment)
if not payment_mode:
invalid_modes.append(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment))
invalid_modes.append(get_link_to_form("Mode of Payment", row.mode_of_payment))
continue
payment_mode[0].default = pos_payment_method.default
append_payment(payment_mode[0])
payment_mode.default = row.default
append_payment(payment_mode)
if invalid_modes:
if invalid_modes == 1:
@ -2006,6 +2020,24 @@ def get_all_mode_of_payments(doc):
where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
{'company': doc.company}, as_dict=1)
def get_mode_of_payments_info(mode_of_payments, company):
data = frappe.db.sql(
"""
select
mpa.default_account, mpa.parent as mop, mp.type as type
from
`tabMode of Payment Account` mpa,`tabMode of Payment` mp
where
mpa.parent = mp.name and
mpa.company = %s and
mp.enabled = 1 and
mp.name in (%s)
group by
mp.name
""", (company, mode_of_payments), as_dict=1)
return {row.get('mop'): row for row in data}
def get_mode_of_payment_info(mode_of_payment, company):
return frappe.db.sql("""
select mpa.default_account, mpa.parent, mp.type as type

View File

@ -2237,9 +2237,9 @@ class TestSalesInvoice(unittest.TestCase):
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
enable_discount_accounting(enable=0)
def test_asset_depreciation_on_sale(self):
def test_asset_depreciation_on_sale_with_pro_rata(self):
"""
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on Sept 30.
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
"""
create_asset_data()
@ -2252,7 +2252,7 @@ class TestSalesInvoice(unittest.TestCase):
expected_values = [
["2020-06-30", 1311.48, 1311.48],
["2021-06-30", 20000.0, 21311.48],
["2021-09-30", 3966.76, 25278.24]
["2021-09-30", 5041.1, 26352.58]
]
for i, schedule in enumerate(asset.schedules):
@ -2261,6 +2261,59 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
def test_asset_depreciation_on_sale_without_pro_rata(self):
"""
Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale.
"""
create_asset_data()
asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1,
available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3,
expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1)
post_depreciation_entries(getdate("2021-09-30"))
create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-12-31"))
asset.load_from_db()
expected_values = [
["2020-12-31", 30000, 30000],
["2021-12-31", 30000, 60000]
]
for i, schedule in enumerate(asset.schedules):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
def test_depreciation_on_return_of_sold_asset(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
create_asset_data()
asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1)
post_depreciation_entries(getdate("2021-09-30"))
si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30"))
return_si = make_return_doc("Sales Invoice", si.name)
return_si.submit()
asset.load_from_db()
expected_values = [
["2020-06-30", 1311.48, 1311.48, True],
["2021-06-30", 20000.0, 21311.48, True],
["2022-06-30", 20000.0, 41311.48, False],
["2023-06-30", 20000.0, 61311.48, False],
["2024-06-30", 20000.0, 81311.48, False],
["2025-06-06", 18688.52, 100000.0, False]
]
for i, schedule in enumerate(asset.schedules):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertEqual(schedule.journal_entry, schedule.journal_entry)
def test_sales_invoice_against_supplier(self):
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,

View File

@ -502,9 +502,11 @@ class Subscription(Document):
# Check invoice dates and make sure it doesn't have outstanding invoices
return getdate() >= getdate(self.current_invoice_start)
def is_current_invoice_generated(self):
def is_current_invoice_generated(self, _current_start_date=None, _current_end_date=None):
invoice = self.get_current_invoice()
_current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True)
if not (_current_start_date and _current_end_date):
_current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True)
if invoice and getdate(_current_start_date) <= getdate(invoice.posting_date) <= getdate(_current_end_date):
return True
@ -523,7 +525,9 @@ class Subscription(Document):
if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice():
self.update_subscription_period(add_days(self.current_invoice_end, 1))
if not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
if not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \
and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
self.generate_invoice(prorate)
@ -559,14 +563,17 @@ class Subscription(Document):
else:
self.set_status_grace_period()
if getdate() > getdate(self.current_invoice_end):
self.update_subscription_period(add_days(self.current_invoice_end, 1))
# Generate invoices periodically even if current invoice are unpaid
if self.generate_new_invoices_past_due_date and not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice()
or self.is_prepaid_to_invoice()):
if self.generate_new_invoices_past_due_date and not \
self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \
and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
self.generate_invoice(prorate)
if getdate() > getdate(self.current_invoice_end):
self.update_subscription_period(add_days(self.current_invoice_end, 1))
@staticmethod
def is_paid(invoice):

View File

@ -58,15 +58,24 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None):
pan_no = ''
parties = []
party_type, party = get_party_details(inv)
has_pan_field = frappe.get_meta(party_type).has_field("pan")
if not tax_withholding_category:
tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan'])
if has_pan_field:
fields = ['tax_withholding_category', 'pan']
else:
fields = ['tax_withholding_category']
tax_withholding_details = frappe.db.get_value(party_type, party, fields, as_dict=1)
tax_withholding_category = tax_withholding_details.get('tax_withholding_category')
pan_no = tax_withholding_details.get('pan')
if not tax_withholding_category:
return
# if tax_withholding_category passed as an argument but not pan_no
if not pan_no:
if not pan_no and has_pan_field:
pan_no = frappe.db.get_value(party_type, party, 'pan')
# Get others suppliers with the same PAN No
@ -174,6 +183,7 @@ def get_lower_deduction_certificate(tax_details, pan_no):
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
{
'pan_no': pan_no,
'tax_withholding_category': tax_details.tax_withholding_category,
'valid_from': ('>=', tax_details.from_date),
'valid_upto': ('<=', tax_details.to_date)
}, 'name')

View File

@ -0,0 +1,16 @@
{
"creation": "2021-10-19 18:06:53.083133",
"docstatus": 0,
"doctype": "Print Format Field Template",
"document_type": "Purchase Invoice",
"field": "taxes",
"idx": 0,
"modified": "2021-10-19 18:06:53.083133",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Taxes",
"owner": "Administrator",
"standard": 1,
"template": "",
"template_file": "templates/print_formats/includes/taxes_and_charges.html"
}

View File

@ -0,0 +1,16 @@
{
"creation": "2021-10-19 17:50:00.152759",
"docstatus": 0,
"doctype": "Print Format Field Template",
"document_type": "Sales Invoice",
"field": "taxes",
"idx": 0,
"modified": "2021-10-19 18:13:20.894207",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Taxes",
"owner": "Administrator",
"standard": 1,
"template": "",
"template_file": "templates/print_formats/includes/taxes_and_charges.html"
}

View File

@ -114,8 +114,9 @@ def prepare_companywise_opening_balance(asset_data, liability_data, equity_data,
# opening_value = Aseet - liability - equity
for data in [asset_data, liability_data, equity_data]:
account_name = get_root_account_name(data[0].root_type, company)
opening_value += (get_opening_balance(account_name, data, company) or 0.0)
if data:
account_name = get_root_account_name(data[0].root_type, company)
opening_value += (get_opening_balance(account_name, data, company) or 0.0)
opening_balance[company] = opening_value

View File

@ -155,6 +155,8 @@ def get_gl_entries(filters, accounting_dimensions):
if filters.get("group_by") == "Group by Voucher":
order_by_statement = "order by posting_date, voucher_type, voucher_no"
if filters.get("group_by") == "Group by Account":
order_by_statement = "order by account, posting_date, creation"
if filters.get("include_default_book_entries"):
filters['company_fb'] = frappe.db.get_value("Company",

View File

@ -450,7 +450,8 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
# new row with references
new_row = journal_entry.append("accounts")
new_row.update(jv_detail.as_dict().copy())
new_row.update((frappe.copy_doc(jv_detail)).as_dict())
new_row.set(d["dr_or_cr"], d["allocated_amount"])
new_row.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit',

View File

@ -75,12 +75,12 @@ class Asset(AccountsController):
if self.is_existing_asset and self.purchase_invoice:
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
def prepare_depreciation_data(self, date_of_sale=None):
def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
self.make_depreciation_schedule(date_of_sale)
self.set_accumulated_depreciation(date_of_sale)
self.set_accumulated_depreciation(date_of_sale, date_of_return)
else:
self.finance_books = []
self.value_after_depreciation = (flt(self.gross_purchase_amount) -
@ -182,7 +182,7 @@ class Asset(AccountsController):
d.precision("rate_of_depreciation"))
def make_depreciation_schedule(self, date_of_sale):
if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules:
if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'):
self.schedules = []
if not self.available_for_use_date:
@ -232,13 +232,15 @@ class Asset(AccountsController):
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
from_date, date_of_sale)
self.append("schedules", {
"schedule_date": date_of_sale,
"depreciation_amount": depreciation_amount,
"depreciation_method": d.depreciation_method,
"finance_book": d.finance_book,
"finance_book_id": d.idx
})
if depreciation_amount > 0:
self.append("schedules", {
"schedule_date": date_of_sale,
"depreciation_amount": depreciation_amount,
"depreciation_method": d.depreciation_method,
"finance_book": d.finance_book,
"finance_book_id": d.idx
})
break
# For first row
@ -257,11 +259,15 @@ class Asset(AccountsController):
self.to_date = add_months(self.available_for_use_date,
n * cint(d.frequency_of_depreciation))
depreciation_amount_without_pro_rata = depreciation_amount
depreciation_amount, days, months = self.get_pro_rata_amt(d,
depreciation_amount, schedule_date, self.to_date)
monthly_schedule_date = add_months(schedule_date, 1)
depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
depreciation_amount, d.finance_book)
monthly_schedule_date = add_months(schedule_date, 1)
schedule_date = add_days(schedule_date, days)
last_schedule_date = schedule_date
@ -397,7 +403,28 @@ class Asset(AccountsController):
frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date")
.format(row.idx))
def set_accumulated_depreciation(self, date_of_sale=None, ignore_booked_entry = False):
# to ensure that final accumulated depreciation amount is accurate
def get_adjusted_depreciation_amount(self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row, finance_book):
depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row(finance_book)
if depreciation_amount_for_first_row + depreciation_amount_for_last_row != depreciation_amount_without_pro_rata:
depreciation_amount_for_last_row = depreciation_amount_without_pro_rata - depreciation_amount_for_first_row
return depreciation_amount_for_last_row
def get_depreciation_amount_for_first_row(self, finance_book):
if self.has_only_one_finance_book():
return self.schedules[0].depreciation_amount
else:
for schedule in self.schedules:
if schedule.finance_book == finance_book:
return schedule.depreciation_amount
def has_only_one_finance_book(self):
if len(self.finance_books) == 1:
return True
def set_accumulated_depreciation(self, date_of_sale=None, date_of_return=None, ignore_booked_entry = False):
straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line']
finance_books = []
@ -414,7 +441,7 @@ class Asset(AccountsController):
value_after_depreciation -= flt(depreciation_amount)
# for the last row, if depreciation method = Straight Line
if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale:
if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale and not date_of_return:
book = self.get('finance_books')[cint(d.finance_book_id) - 1]
depreciation_amount += flt(value_after_depreciation -
flt(book.expected_value_after_useful_life), d.precision("depreciation_amount"))
@ -833,7 +860,7 @@ def get_depreciation_amount(asset, depreciable_value, row):
if row.depreciation_method in ("Straight Line", "Manual"):
# if the Depreciation Schedule is being prepared for the first time
if not asset.flags.increase_in_asset_life:
depreciation_amount = (flt(row.value_after_depreciation) -
depreciation_amount = (flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) -
flt(row.expected_value_after_useful_life)) / depreciation_left
# if the Depreciation Schedule is being modified after Asset Repair

File diff suppressed because it is too large Load Diff

View File

@ -16,9 +16,8 @@ frappe.query_reports["Fixed Asset Register"] = {
fieldname:"status",
label: __("Status"),
fieldtype: "Select",
options: "In Location\nDisposed",
default: 'In Location',
reqd: 1
options: "\nIn Location\nDisposed",
default: 'In Location'
},
{
"fieldname":"filter_based_on",

View File

@ -45,12 +45,13 @@ def get_conditions(filters):
if filters.get('cost_center'):
conditions["cost_center"] = filters.get('cost_center')
# In Store assets are those that are not sold or scrapped
operand = 'not in'
if status not in 'In Location':
operand = 'in'
if status:
# In Store assets are those that are not sold or scrapped
operand = 'not in'
if status not in 'In Location':
operand = 'in'
conditions['status'] = (operand, ['Sold', 'Scrapped'])
conditions['status'] = (operand, ['Sold', 'Scrapped'])
return conditions

View File

@ -0,0 +1,16 @@
{
"creation": "2021-10-19 18:07:19.253457",
"docstatus": 0,
"doctype": "Print Format Field Template",
"document_type": "Purchase Order",
"field": "taxes",
"idx": 0,
"modified": "2021-10-19 18:07:19.253457",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Taxes",
"owner": "Administrator",
"standard": 1,
"template": "",
"template_file": "templates/print_formats/includes/taxes_and_charges.html"
}

View File

@ -0,0 +1,16 @@
{
"creation": "2021-10-19 18:09:08.103919",
"docstatus": 0,
"doctype": "Print Format Field Template",
"document_type": "Supplier Quotation",
"field": "taxes",
"idx": 0,
"modified": "2021-10-19 18:09:08.103919",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Taxes",
"owner": "Administrator",
"standard": 1,
"template": "",
"template_file": "templates/print_formats/includes/taxes_and_charges.html"
}

View File

@ -41,10 +41,13 @@ def get_conditions(filters):
if filters.get("from_date") and filters.get("to_date"):
conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
for field in ['company', 'name', 'status']:
for field in ['company', 'name']:
if filters.get(field):
conditions += f" and po.{field} = %({field})s"
if filters.get('status'):
conditions += " and po.status in %(status)s"
if filters.get('project'):
conditions += " and poi.project = %(project)s"

View File

@ -815,6 +815,38 @@ class AccountsController(TransactionBase):
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
unlink_ref_doc_from_payment_entries(self)
if self.doctype == "Sales Order":
self.unlink_ref_doc_from_po()
def unlink_ref_doc_from_po(self):
so_items = []
for item in self.items:
so_items.append(item.name)
linked_po = list(set(frappe.get_all(
'Purchase Order Item',
filters = {
'sales_order': self.name,
'sales_order_item': ['in', so_items],
'docstatus': ['<', 2]
},
pluck='parent'
)))
if linked_po:
frappe.db.set_value(
'Purchase Order Item', {
'sales_order': self.name,
'sales_order_item': ['in', so_items],
'docstatus': ['<', 2]
},{
'sales_order': None,
'sales_order_item': None
}
)
frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po)))
def get_tax_map(self):
tax_map = {}
for tax in self.get('taxes'):
@ -1354,8 +1386,8 @@ class AccountsController(TransactionBase):
total = 0
base_total = 0
for d in self.get("payment_schedule"):
total += flt(d.payment_amount)
base_total += flt(d.base_payment_amount)
total += flt(d.payment_amount, d.precision("payment_amount"))
base_total += flt(d.base_payment_amount, d.precision("base_payment_amount"))
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
grand_total = self.get("rounded_total") or self.grand_total
@ -1371,8 +1403,9 @@ class AccountsController(TransactionBase):
else:
grand_total -= self.get("total_advance")
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
if total != flt(grand_total, self.precision("grand_total")) or \
base_total != flt(base_grand_total, self.precision("base_grand_total")):
if flt(total, self.precision("grand_total")) != flt(grand_total, self.precision("grand_total")) or \
flt(base_total, self.precision("base_grand_total")) != flt(base_grand_total, self.precision("base_grand_total")):
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
def is_rounded_total_disabled(self):

View File

@ -132,7 +132,8 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select {field} from `tabSupplier`
where docstatus < 2
and ({key} like %(txt)s
or supplier_name like %(txt)s) and disabled=0
or supplier_name like %(txt)s) and disabled=0
and (on_hold = 0 or (on_hold = 1 and CURDATE() > release_date))
{mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@ -565,7 +566,7 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters)
query_filters.append(['name', query_selector, dimensions])
output = frappe.get_all(doctype, filters=query_filters)
output = frappe.get_list(doctype, filters=query_filters)
result = [d.name for d in output]
return [(d,) for d in set(result)]

View File

@ -260,7 +260,9 @@ class calculate_taxes_and_totals(object):
self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
def calculate_taxes(self):
self.doc.rounding_adjustment = 0
if not self.doc.get('is_consolidated'):
self.doc.rounding_adjustment = 0
# maintain actual tax rate based on idx
actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
@ -312,7 +314,9 @@ class calculate_taxes_and_totals(object):
# adjust Discount Amount loss in last tax iteration
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
and self.doc.discount_amount \
and self.doc.apply_discount_on == "Grand Total" \
and not self.doc.get('is_consolidated'):
self.doc.rounding_adjustment = flt(self.doc.grand_total
- flt(self.doc.discount_amount) - tax.total,
self.doc.precision("rounding_adjustment"))
@ -405,11 +409,16 @@ class calculate_taxes_and_totals(object):
self.doc.rounding_adjustment = diff
def calculate_totals(self):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
if self.doc.get("taxes") else flt(self.doc.net_total)
if self.doc.get("taxes"):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
else:
self.doc.grand_total = flt(self.doc.net_total)
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
if self.doc.get("taxes"):
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
- flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
else:
self.doc.total_taxes_and_charges = 0.0
self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
@ -446,19 +455,20 @@ class calculate_taxes_and_totals(object):
self.doc.total_net_weight += d.total_weight
def set_rounded_total(self):
if self.doc.meta.get_field("rounded_total"):
if self.doc.is_rounded_total_disabled():
self.doc.rounded_total = self.doc.base_rounded_total = 0
return
if not self.doc.get('is_consolidated'):
if self.doc.meta.get_field("rounded_total"):
if self.doc.is_rounded_total_disabled():
self.doc.rounded_total = self.doc.base_rounded_total = 0
return
self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
self.doc.currency, self.doc.precision("rounded_total"))
self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
self.doc.currency, self.doc.precision("rounded_total"))
#if print_in_rate is set, we would have already calculated rounding adjustment
self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
self.doc.precision("rounding_adjustment"))
#if print_in_rate is set, we would have already calculated rounding adjustment
self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
self.doc.precision("rounding_adjustment"))
self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
def _cleanup(self):
if not self.doc.get('is_consolidated'):

View File

@ -314,6 +314,8 @@ def make_request_for_quotation(source_name, target_doc=None):
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
def set_missing_values(source, target):
target.opportunity_name = source.name
if source.opportunity_from == "Lead":
target.lead_name = source.party_name

View File

@ -20,6 +20,7 @@
"website",
"column_break_13",
"prospect_owner",
"company",
"leads_section",
"prospect_lead",
"address_and_contact_section",
@ -153,14 +154,23 @@
"fieldname": "address_and_contact_section",
"fieldtype": "Section Break",
"label": "Address and Contact"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-08-27 16:24:42.961967",
"migration_hash": "f39fb8f4e18a0e7fd391f0b4b52d8375",
"modified": "2021-11-01 13:10:36.759249",
"modified_by": "Administrator",
"module": "CRM",
"name": "Prospect",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{

View File

@ -60,7 +60,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -144,7 +143,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -226,7 +224,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -308,7 +305,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -390,7 +386,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -472,7 +467,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -554,7 +548,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -636,7 +629,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -718,7 +710,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -800,7 +791,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -882,7 +872,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -964,7 +953,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1046,7 +1034,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1128,7 +1115,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1210,7 +1196,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1292,7 +1277,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1374,7 +1358,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1456,7 +1439,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1538,7 +1520,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1620,7 +1601,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1702,7 +1682,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1784,7 +1763,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1866,7 +1844,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -1948,7 +1925,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2030,7 +2006,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2112,7 +2087,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2194,7 +2168,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2276,7 +2249,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2358,7 +2330,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2440,7 +2411,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2522,7 +2492,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2604,7 +2573,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2686,7 +2654,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2768,7 +2735,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2850,7 +2816,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -2932,7 +2897,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3014,7 +2978,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3098,7 +3061,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3180,7 +3142,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3262,7 +3223,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3344,7 +3304,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3426,7 +3385,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3508,7 +3466,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3590,7 +3547,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3672,7 +3628,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3754,7 +3709,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3836,7 +3790,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -3918,7 +3871,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4000,7 +3952,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4082,7 +4033,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4164,7 +4114,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4246,7 +4195,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4328,7 +4276,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4410,7 +4357,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4492,7 +4438,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4574,7 +4519,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4656,7 +4600,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4738,7 +4681,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4820,7 +4762,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4902,7 +4843,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -4984,7 +4924,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -5066,7 +5005,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@ -5148,7 +5086,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
"synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,

View File

@ -156,6 +156,8 @@ def get_employees_having_an_event_today(event_type):
DAY({condition_column}) = DAY(%(today)s)
AND
MONTH({condition_column}) = MONTH(%(today)s)
AND
YEAR({condition_column}) < YEAR(%(today)s)
AND
`status` = 'Active'
""",
@ -166,6 +168,8 @@ def get_employees_having_an_event_today(event_type):
DATE_PART('day', {condition_column}) = date_part('day', %(today)s)
AND
DATE_PART('month', {condition_column}) = date_part('month', %(today)s)
AND
DATE_PART('year', {condition_column}) < date_part('year', %(today)s)
AND
"status" = 'Active'
""",

View File

@ -100,7 +100,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-09-23 20:27:36.027728",
"modified": "2021-10-26 20:27:36.027728",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Taxes and Charges",

View File

@ -1,19 +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
@frappe.whitelist()
def enable_hub():
hub_settings = frappe.get_doc('Marketplace Settings')
hub_settings.register()
frappe.db.commit()
return hub_settings
@frappe.whitelist()
def sync():
hub_settings = frappe.get_doc('Marketplace Settings')
hub_settings.sync()

View File

@ -1,233 +0,0 @@
from __future__ import unicode_literals
import json
import frappe
from frappe import _
from frappe.desk.form.load import get_attachments
from frappe.frappeclient import FrappeClient
from six import string_types
current_user = frappe.session.user
@frappe.whitelist()
def register_marketplace(company, company_description):
validate_registerer()
settings = frappe.get_single('Marketplace Settings')
message = settings.register_seller(company, company_description)
if message.get('hub_seller_name'):
settings.registered = 1
settings.hub_seller_name = message.get('hub_seller_name')
settings.save()
settings.add_hub_user(frappe.session.user)
return { 'ok': 1 }
@frappe.whitelist()
def register_users(user_list):
user_list = json.loads(user_list)
settings = frappe.get_single('Marketplace Settings')
for user in user_list:
settings.add_hub_user(user)
return user_list
def validate_registerer():
if current_user == 'Administrator':
frappe.throw(_('Please login as another user to register on Marketplace'))
valid_roles = ['System Manager', 'Item Manager']
if not frappe.utils.is_subset(valid_roles, frappe.get_roles()):
frappe.throw(_('Only users with {0} role can register on Marketplace').format(', '.join(valid_roles)),
frappe.PermissionError)
@frappe.whitelist()
def call_hub_method(method, params=None):
connection = get_hub_connection()
if isinstance(params, string_types):
params = json.loads(params)
params.update({
'cmd': 'hub.hub.api.' + method
})
response = connection.post_request(params)
return response
def map_fields(items):
field_mappings = get_field_mappings()
table_fields = [d.fieldname for d in frappe.get_meta('Item').get_table_fields()]
hub_seller_name = frappe.db.get_value('Marketplace Settings', 'Marketplace Settings', 'hub_seller_name')
for item in items:
for fieldname in table_fields:
item.pop(fieldname, None)
for mapping in field_mappings:
local_fieldname = mapping.get('local_fieldname')
remote_fieldname = mapping.get('remote_fieldname')
value = item.get(local_fieldname)
item.pop(local_fieldname, None)
item[remote_fieldname] = value
item['doctype'] = 'Hub Item'
item['hub_seller'] = hub_seller_name
item.pop('attachments', None)
return items
@frappe.whitelist()
def get_valid_items(search_value=''):
items = frappe.get_list(
'Item',
fields=["*"],
filters={
'disabled': 0,
'item_name': ['like', '%' + search_value + '%'],
'publish_in_hub': 0
},
order_by="modified desc"
)
valid_items = filter(lambda x: x.image and x.description, items)
def prepare_item(item):
item.source_type = "local"
item.attachments = get_attachments('Item', item.item_code)
return item
valid_items = map(prepare_item, valid_items)
return valid_items
@frappe.whitelist()
def update_item(ref_doc, data):
data = json.loads(data)
data.update(dict(doctype='Hub Item', name=ref_doc))
try:
connection = get_hub_connection()
connection.update(data)
except Exception as e:
frappe.log_error(message=e, title='Hub Sync Error')
@frappe.whitelist()
def publish_selected_items(items_to_publish):
items_to_publish = json.loads(items_to_publish)
items_to_update = []
if not len(items_to_publish):
frappe.throw(_('No items to publish'))
for item in items_to_publish:
item_code = item.get('item_code')
frappe.db.set_value('Item', item_code, 'publish_in_hub', 1)
hub_dict = {
'doctype': 'Hub Tracked Item',
'item_code': item_code,
'published': 1,
'hub_category': item.get('hub_category'),
'image_list': item.get('image_list')
}
frappe.get_doc(hub_dict).insert(ignore_if_duplicate=True)
items = map_fields(items_to_publish)
try:
item_sync_preprocess(len(items))
convert_relative_image_urls_to_absolute(items)
# TODO: Publish Progress
connection = get_hub_connection()
connection.insert_many(items)
item_sync_postprocess()
except Exception as e:
frappe.log_error(message=e, title='Hub Sync Error')
@frappe.whitelist()
def unpublish_item(item_code, hub_item_name):
''' Remove item listing from the marketplace '''
response = call_hub_method('unpublish_item', {
'hub_item_name': hub_item_name
})
if response:
frappe.db.set_value('Item', item_code, 'publish_in_hub', 0)
frappe.delete_doc('Hub Tracked Item', item_code)
else:
frappe.throw(_('Unable to update remote activity'))
@frappe.whitelist()
def get_unregistered_users():
settings = frappe.get_single('Marketplace Settings')
registered_users = [user.user for user in settings.users] + ['Administrator', 'Guest']
all_users = [user.name for user in frappe.db.get_all('User', filters={'enabled': 1})]
unregistered_users = [user for user in all_users if user not in registered_users]
return unregistered_users
def item_sync_preprocess(intended_item_publish_count):
response = call_hub_method('pre_items_publish', {
'intended_item_publish_count': intended_item_publish_count
})
if response:
frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1)
return response
else:
frappe.throw(_('Unable to update remote activity'))
def item_sync_postprocess():
response = call_hub_method('post_items_publish', {})
if response:
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now())
else:
frappe.throw(_('Unable to update remote activity'))
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0)
def convert_relative_image_urls_to_absolute(items):
from six.moves.urllib.parse import urljoin
for item in items:
file_path = item['image']
if file_path.startswith('/files/'):
item['image'] = urljoin(frappe.utils.get_url(), file_path)
def get_hub_connection():
settings = frappe.get_single('Marketplace Settings')
marketplace_url = settings.marketplace_url
hub_user = settings.get_hub_user(frappe.session.user)
if hub_user:
password = hub_user.get_password()
hub_connection = FrappeClient(marketplace_url, hub_user.user, password)
return hub_connection
else:
read_only_hub_connection = FrappeClient(marketplace_url)
return read_only_hub_connection
def get_field_mappings():
return []

View File

@ -1,50 +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"
},
{
"is_child_table": 0,
"local_fieldname": "company_logo",
"remote_fieldname": "company_logo"
}
],
"idx": 2,
"local_doctype": "Company",
"mapping_name": "Company to Hub Company",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"name": "Company to Hub Company",
"owner": "Administrator",
"page_length": 10,
"remote_objectname": "Hub Company",
"remote_primary_key": "name"
}

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": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"name": "Hub Message to Lead",
"owner": "Administrator",
"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_value('Hub Settings' , 'Hub Settings', 'company_email')",
"remote_fieldname": "hub_seller"
},
{
"is_child_table": 0,
"local_fieldname": "image",
"remote_fieldname": "image"
},
{
"is_child_table": 0,
"local_fieldname": "image_list",
"remote_fieldname": "image_list"
},
{
"is_child_table": 0,
"local_fieldname": "item_group",
"remote_fieldname": "item_group"
},
{
"is_child_table": 0,
"local_fieldname": "hub_category",
"remote_fieldname": "hub_category"
}
],
"idx": 1,
"local_doctype": "Item",
"mapping_name": "Item to Hub Item",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2018-08-19 22:20:25.727581",
"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,19 +0,0 @@
{
"creation": "2017-09-07 11:39:38.445902",
"docstatus": 0,
"doctype": "Data Migration Plan",
"idx": 1,
"mappings": [
{
"enabled": 1,
"mapping": "Item to Hub Item"
}
],
"modified": "2018-08-19 22:20:25.644602",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Sync",
"owner": "Administrator",
"plan_name": "Hub Sync",
"postprocess_method": "erpnext.hub_node.api.item_sync_postprocess"
}

View File

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

View File

@ -1,210 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:item_code",
"beta": 0,
"creation": "2018-03-18 09:33:50.267762",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_code",
"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": "Item Code",
"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": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hub_category",
"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": "Hub Category",
"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": "published",
"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": "Published",
"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": "image_list",
"fieldtype": "Long Text",
"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": "Image List",
"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
}
],
"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": "2019-12-10 11:37:35.951019",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Tracked Item",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 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
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Item Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 1,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

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

View File

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

View File

@ -1,140 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2018-08-31 12:36:45.627531",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 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": 1,
"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": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hub_user_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": "Hub User",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "password",
"fieldtype": "Password",
"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": "Hub Password",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub User",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"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,
"track_views": 0
}

View File

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

View File

@ -1,72 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-03-06 04:38:49.891787",
"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": "user",
"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": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"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
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Users",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"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,11 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
class HubUsers(Document):
pass

View File

@ -1,8 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Marketplace Settings', {
refresh: function(frm) {
$('#toolbar-user .marketplace-link').toggle(!frm.doc.disable_marketplace);
},
});

View File

@ -1,410 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2018-08-31 15:54:38.795263",
"custom": 0,
"description": "",
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disable_marketplace",
"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 Marketplace",
"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,
"depends_on": "eval:!doc.disable_marketplace",
"fieldname": "marketplace_settings_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": "Marketplace Settings",
"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,
"default": "https://hubmarket.org",
"fieldname": "marketplace_url",
"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": "Marketplace URL (to hide and update label)",
"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": "registered",
"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": "Registered",
"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": "sync_in_progress",
"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": "Sync in Progress",
"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": "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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "hub_seller_name",
"fieldtype": "Data",
"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": "Hub Seller 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": 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": "users",
"fieldtype": "Table",
"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": "Users",
"length": 0,
"no_copy": 0,
"options": "Hub User",
"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,
"depends_on": "",
"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": 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,
"default": "",
"depends_on": "eval:1",
"fieldname": "custom_data",
"fieldtype": "Code",
"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": "Custom Data",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Marketplace Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 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
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"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,
"track_views": 0
}

View File

@ -1,93 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import json
import frappe
from frappe.frappeclient import FrappeClient
from frappe.model.document import Document
from frappe.utils import cint
class MarketplaceSettings(Document):
def register_seller(self, company, company_description):
country, currency, company_logo = frappe.db.get_value('Company', company,
['country', 'default_currency', 'company_logo'])
company_details = {
'company': company,
'country': country,
'currency': currency,
'company_description': company_description,
'company_logo': company_logo,
'site_name': frappe.utils.get_url()
}
hub_connection = self.get_connection()
response = hub_connection.post_request({
'cmd': 'hub.hub.api.add_hub_seller',
'company_details': json.dumps(company_details)
})
return response
def add_hub_user(self, user_email):
'''Create a Hub User and User record on hub server
and if successfull append it to Hub User table
'''
if not self.registered:
return
hub_connection = self.get_connection()
first_name, last_name = frappe.db.get_value('User', user_email, ['first_name', 'last_name'])
hub_user = hub_connection.post_request({
'cmd': 'hub.hub.api.add_hub_user',
'user_email': user_email,
'first_name': first_name,
'last_name': last_name,
'hub_seller': self.hub_seller_name
})
self.append('users', {
'user': hub_user.get('user_email'),
'hub_user_name': hub_user.get('hub_user_name'),
'password': hub_user.get('password')
})
self.save()
def get_hub_user(self, user):
'''Return the Hub User doc from the `users` table if password is set'''
filtered_users = list(filter(
lambda x: x.user == user and x.password,
self.users
))
if filtered_users:
return filtered_users[0]
def get_connection(self):
return FrappeClient(self.marketplace_url)
def unregister(self):
"""Disable the User on hubmarket.org"""
@frappe.whitelist()
def is_marketplace_enabled():
if not hasattr(frappe.local, 'is_marketplace_enabled'):
frappe.local.is_marketplace_enabled = cint(frappe.db.get_single_value('Marketplace Settings',
'disable_marketplace'))
return frappe.local.is_marketplace_enabled

View File

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

View File

@ -1,148 +0,0 @@
from __future__ import unicode_literals
import json
import frappe
from frappe.contacts.doctype.contact.contact import get_default_contact
from frappe.frappeclient import FrappeClient
from frappe.utils import nowdate
from frappe.utils.nestedset import get_root_of
def get_list(doctype, start, limit, fields, filters, order_by):
pass
def get_hub_connection():
if frappe.db.exists('Data Migration Connector', 'Hub Connector'):
hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
hub_connection = hub_connector.get_connection()
return hub_connection.connection
# read-only connection
hub_connection = FrappeClient(frappe.conf.hub_url)
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.opportunity_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_group': supplier.supplier_group,
'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,
'is_primary_contact': 1,
'links': [
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
]
})
contact.add_email(supplier.supplier_email, is_primary=True)
contact.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('Marketplace 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('Marketplace Settings', 'company'),
'email_id': frappe.db.get_single_value('Marketplace 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

@ -436,7 +436,7 @@
"description": "Item Image (if not slideshow)",
"fieldname": "website_image",
"fieldtype": "Attach Image",
"label": "Image"
"label": "Website Image"
},
{
"allow_on_submit": 1,
@ -539,7 +539,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2021-05-16 12:25:09.081968",
"modified": "2021-10-27 14:52:04.500251",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@ -307,6 +307,9 @@ class BOM(WebsiteGenerator):
existing_bom_cost = self.total_cost
for d in self.get("items"):
if not d.item_code:
continue
rate = self.get_rm_rate({
"company": self.company,
"item_code": d.item_code,
@ -599,7 +602,7 @@ class BOM(WebsiteGenerator):
for d in self.get('items'):
if d.bom_no:
self.get_child_exploded_items(d.bom_no, d.stock_qty)
else:
elif d.item_code:
self.add_to_cur_exploded_items(frappe._dict({
'item_code' : d.item_code,
'item_name' : d.item_name,

View File

@ -605,7 +605,8 @@ def make_material_request(source_name, target_doc=None):
"doctype": "Material Request Item",
"field_map": {
"required_qty": "qty",
"uom": "stock_uom"
"uom": "stock_uom",
"name": "job_card_item"
},
"postprocess": update_item,
}

View File

@ -311,7 +311,7 @@ class ProductionPlan(Document):
if self.total_produced_qty > 0:
self.status = "In Process"
if self.total_produced_qty >= self.total_planned_qty:
if self.check_have_work_orders_completed():
self.status = "Completed"
if self.status != 'Completed':
@ -575,6 +575,15 @@ class ProductionPlan(Document):
self.append("sub_assembly_items", data)
def check_have_work_orders_completed(self):
wo_status = frappe.db.get_list(
"Work Order",
filters={"production_plan": self.name},
fields="status",
pluck="status"
)
return all(s == "Completed" for s in wo_status)
@frappe.whitelist()
def download_raw_materials(doc, warehouses=None):
if isinstance(doc, str):

View File

@ -182,6 +182,7 @@
"reqd": 1
},
{
"default": "1.0",
"fieldname": "qty",
"fieldtype": "Float",
"label": "Qty To Manufacture",
@ -572,10 +573,11 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2021-08-24 15:14:03.844937",
"modified": "2021-10-27 19:21:35.139888",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
"naming_rule": "By \"Naming Series\" field",
"nsm_parent_field": "parent_work_order",
"owner": "Administrator",
"permissions": [

View File

@ -685,9 +685,7 @@ class WorkOrder(Document):
if not d.operation:
d.operation = operation
else:
# Attribute a big number (999) to idx for sorting putpose in case idx is NULL
# For instance in BOM Explosion Item child table, the items coming from sub assembly items
for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
for item in sorted(item_dict.values(), key=lambda d: d['idx'] or float('inf')):
self.append('required_items', {
'rate': item.rate,
'amount': item.rate * item.qty,

View File

@ -20,9 +20,8 @@ Agriculture
ERPNext Integrations
Non Profit
Hotels
Hub Node
Quality Management
Communication
Loan Management
Payroll
Telephony
Telephony

View File

@ -58,11 +58,7 @@ erpnext.patches.v11_0.set_department_for_doctypes
erpnext.patches.v11_0.update_allow_transfer_for_manufacture
erpnext.patches.v11_0.add_item_group_defaults
erpnext.patches.v11_0.add_expense_claim_default_account
execute:frappe.delete_doc("Page", "hub")
erpnext.patches.v11_0.reset_publish_in_hub_for_all_items
erpnext.patches.v11_0.update_hub_url # 2018-08-31 # 2018-09-03
erpnext.patches.v11_0.make_job_card
erpnext.patches.v10_0.delete_hub_documents # 12-08-2018
erpnext.patches.v11_0.add_default_dispatch_notification_template
erpnext.patches.v11_0.add_market_segments
erpnext.patches.v11_0.add_sales_stages
@ -153,7 +149,6 @@ erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim
erpnext.patches.v12_0.add_eway_bill_in_delivery_note
erpnext.patches.v12_0.set_lead_title_field
erpnext.patches.v12_0.set_permission_einvoicing
erpnext.patches.v12_0.set_published_in_hub_tracked_item
erpnext.patches.v12_0.set_job_offer_applicant_email
erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
@ -308,6 +303,9 @@ erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
erpnext.patches.v13_0.add_default_interview_notification_templates
erpnext.patches.v13_0.enable_scheduler_job_for_item_reposting
erpnext.patches.v13_0.requeue_failed_reposts
erpnext.patches.v12_0.update_production_plan_status
erpnext.patches.v13_0.healthcare_deprecation_warning
erpnext.patches.v14_0.delete_healthcare_doctypes
erpnext.patches.v13_0.update_category_in_ltds_certificate
erpnext.patches.v13_0.create_pan_field_for_india #2
erpnext.patches.v14_0.delete_hub_doctypes

View File

@ -1,19 +0,0 @@
from __future__ import unicode_literals
import frappe
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)
docs = frappe.get_all("Data Migration Run", filters={"data_migration_plan": plan.name})
for doc in docs:
frappe.delete_doc("Data Migration Run", doc.name)
frappe.delete_doc("Data Migration Plan", plan.name)

View File

@ -1,8 +0,0 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
frappe.db.sql("""update `tabItem` set publish_in_hub = 0""")

View File

@ -1,8 +0,0 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('hub_node', 'doctype', 'Marketplace Settings')
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'marketplace_url', 'https://hubmarket.org')

View File

@ -1,14 +0,0 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item")
if not frappe.db.a_row_exists("Hub Tracked Item"):
return
frappe.db.sql('''
Update `tabHub Tracked Item`
SET published = 1
''')

View File

@ -0,0 +1,31 @@
# Copyright (c) 2021, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
def execute():
frappe.reload_doc("manufacturing", "doctype", "production_plan")
frappe.db.sql("""
UPDATE `tabProduction Plan` ppl
SET status = "Completed"
WHERE ppl.name IN (
SELECT ss.name FROM (
SELECT
(
count(wo.status = "Completed") =
count(pp.name)
) =
(
pp.status != "Completed"
AND pp.total_produced_qty >= pp.total_planned_qty
) AS should_set,
pp.name AS name
FROM
`tabWork Order` wo INNER JOIN`tabProduction Plan` pp
ON wo.production_plan = pp.name
GROUP BY pp.name
HAVING should_set = 1
) ss
)
""")

View File

@ -0,0 +1,20 @@
import frappe
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
frappe.reload_doc('regional', 'doctype', 'lower_deduction_certificate')
ldc = frappe.qb.DocType("Lower Deduction Certificate").as_("ldc")
supplier = frappe.qb.DocType("Supplier")
frappe.qb.update(ldc).inner_join(supplier).on(
ldc.supplier == supplier.name
).set(
ldc.tax_withholding_category, supplier.tax_withholding_category
).where(
ldc.tax_withholding_category.isnull()
).run()

View File

@ -0,0 +1,10 @@
import frappe
def execute():
doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck='name')
for doctype in doctypes:
frappe.delete_doc("DocType", doctype, ignore_missing=True)
frappe.delete_doc("Module Def", "Hub Node", ignore_missing=True, force=True)

View File

@ -1,14 +1,10 @@
{
"css/erpnext.css": [
"public/less/erpnext.less",
"public/less/hub.less",
"public/scss/call_popup.scss",
"public/scss/point-of-sale.scss",
"public/scss/hierarchy_chart.scss"
],
"css/marketplace.css": [
"public/less/hub.less"
],
"js/erpnext-web.min.js": [
"public/js/website_utils.js",
"public/js/shopping_cart.js"
@ -17,9 +13,6 @@
"public/scss/website.scss",
"public/scss/shopping_cart.scss"
],
"js/marketplace.min.js": [
"public/js/hub/marketplace.js"
],
"js/erpnext.min.js": [
"public/js/conf.js",
"public/js/utils.js",
@ -41,7 +34,6 @@
"public/js/utils/supplier_quick_entry.js",
"public/js/education/student_button.html",
"public/js/education/assessment_result_tool.html",
"public/js/hub/hub_factory.js",
"public/js/call_popup/call_popup.js",
"public/js/utils/dimension_tree_filter.js",
"public/js/telephony.js",

View File

@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="330"
height="345.43808"
viewBox="0 0 87.312496 91.397155"
version="1.1"
id="svg4635"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="hub-logo.svg"
inkscape:export-filename="/home/raghu/Desktop/hub-logo.png"
inkscape:export-xdpi="95.878258"
inkscape:export-ydpi="95.878258">
<defs
id="defs4629" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="234.27717"
inkscape:cy="167.57445"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1149"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata4632">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(121.51931,-138.66452)">
<rect
rx="13.229166"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/home/raghu/Desktop/send/hub-02.png"
style="opacity:1;vector-effect:none;fill:#89da29;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
id="rect828"
width="87.3125"
height="87.3125"
x="-121.51931"
y="142.74918"
ry="13.229166" />
<path
style="opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
clip-path="none"
d="m -121.51931,202.96343 v 13.86892 c 0,7.32897 5.90017,13.22917 13.22916,13.22917 h 60.854162 c 6.610072,0 12.056133,-4.80013 13.061216,-11.1187 -43.339761,0.1608 -54.359752,-16.03276 -87.144538,-15.97939 z"
id="path830"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
clip-path="none"
d="m -34.20681,202.96343 c -32.784694,-0.0533 -43.804846,16.14019 -87.14455,15.97939 1.00509,6.31857 6.45115,11.1187 13.06122,11.1187 h 60.854164 c 7.328992,0 13.229166,-5.9002 13.229166,-13.22917 z"
id="path832"
inkscape:connector-curvature="0" />
<path
id="path834"
d="m -84.351263,175.75725 c -1.30945,0 -2.376091,1.06665 -2.376091,2.37608 v 10.02885 0.001 c 0.06583,4.83083 4.01156,8.73477 8.857351,8.73486 4.8718,5e-5 8.846821,-3.94421 8.871295,-8.81134 v -0.001 -9.95288 c 0,-1.30943 -1.066113,-2.37557 -2.375589,-2.37557 -1.309396,0 -2.376064,1.06614 -2.376064,2.37557 v 9.8888 c 0,2.26045 -1.858169,4.10983 -4.119642,4.10983 -2.263616,0 -4.105699,-1.82766 -4.105699,-4.08968 v -9.90844 c 0,-1.30943 -1.066138,-2.37608 -2.375561,-2.37608 z m -20.887107,0.0925 c -1.30943,0 -2.37609,1.06717 -2.37609,2.3766 v 16.45119 c 0,1.30944 1.06666,2.37609 2.37609,2.37609 1.30945,0 2.37556,-1.06665 2.37556,-2.37609 v -5.97327 h 8.22534 v 5.97327 c 0,1.30944 1.066641,2.37609 2.376091,2.37609 1.309423,0 2.375561,-1.06665 2.375561,-2.37609 v -16.45119 c 0,-1.30943 -1.066138,-2.3766 -2.375561,-2.3766 -1.30945,0 -2.376091,1.06717 -2.376091,2.3766 v 5.72627 h -8.22534 v -5.72627 c 0,-1.30943 -1.06611,-2.3766 -2.37556,-2.3766 z m 41.77419,0 c -0.654712,0 -1.248675,0.26711 -1.678967,0.69764 -0.05368,0.0537 -0.105119,0.10983 -0.153458,0.16846 v 5.3e-4 c -0.04839,0.0586 -0.09427,0.11929 -0.136949,0.18242 v 5.3e-4 c -0.256381,0.37936 -0.406691,0.83617 -0.406691,1.32705 v 16.45119 c 0,0.1635 0.01693,0.3242 0.04858,0.47852 0.09512,0.46331 0.32594,0.87828 0.64852,1.20096 0.161369,0.16136 0.345308,0.29938 0.547264,0.40928 v 0 c 0.134567,0.0732 0.276781,0.13403 0.425318,0.18035 v 0 c 0.148537,0.0463 0.303186,0.0783 0.462518,0.0946 v 0 c 0.07959,0.008 0.160708,0.0124 0.242358,0.0124 h 8.33181 c 0.08747,0 0.167931,-0.0145 0.251142,-0.0238 l 0.09509,0.005 c 0.06019,0.003 0.119407,0.005 0.178779,0.006 h 0.0037 0.0048 c 3.578305,-2e-5 6.487954,-2.90916 6.487981,-6.48747 v -0.001 c -0.0026,-1.51334 -0.578009,-2.9475 -1.540484,-4.10673 0.962448,-1.15892 1.537785,-2.59314 1.540484,-4.10621 v -0.001 c -2.7e-5,-3.57831 -2.909676,-6.48744 -6.487981,-6.48746 h -0.533294 z m 8.865103,4.75062 c 0.96393,0 1.736831,0.77394 1.736831,1.73788 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 v 0 h -0.532792 -5.955718 v -3.47317 h 5.956248 z m 0,8.21552 v 0 c 0.963507,5.3e-4 1.735799,0.77373 1.735799,1.73736 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 h -0.0048 l -0.533294,0.0119 h -5.951591 v -3.4742 h 5.959846 z"
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.93750048;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -77.859375,138.66406 c -9.653316,0 -18.439915,3.93483 -24.767575,10.28125 a 3.9691471,3.9691471 0 1 0 5.621091,5.60352 c 4.899576,-4.9141 11.6422,-7.94727 19.146484,-7.94727 7.501101,0 14.241542,3.03098 19.140625,7.94141 a 3.9691471,3.9691471 0 1 0 5.619141,-5.60547 c -6.327038,-6.34169 -15.110547,-10.27344 -24.759766,-10.27344 z"
id="path838"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -18,7 +18,6 @@ import "./utils/customer_quick_entry";
import "./utils/supplier_quick_entry";
import "./education/student_button.html";
import "./education/assessment_result_tool.html";
import "./hub/hub_factory";
import "./call_popup/call_popup";
import "./utils/dimension_tree_filter";
import "./telephony";

View File

@ -113,15 +113,15 @@ function get_filters() {
"fieldname":"period_start_date",
"label": __("Start Date"),
"fieldtype": "Date",
"hidden": 1,
"reqd": 1
"reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Date Range'"
},
{
"fieldname":"period_end_date",
"label": __("End Date"),
"fieldtype": "Date",
"hidden": 1,
"reqd": 1
"reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Date Range'"
},
{
"fieldname":"from_fiscal_year",
@ -129,7 +129,8 @@ function get_filters() {
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"reqd": 1
"reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
},
{
"fieldname":"to_fiscal_year",
@ -137,7 +138,8 @@ function get_filters() {
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"reqd": 1
"reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
},
{
"fieldname": "periodicity",

View File

@ -1,119 +0,0 @@
<template>
<div class="hub-page-container">
<component :is="current_page.component" :key="current_page.key"></component>
</div>
</template>
<script>
import Home from './pages/Home.vue';
import Search from './pages/Search.vue';
import Category from './pages/Category.vue';
import SavedItems from './pages/SavedItems.vue';
import FeaturedItems from './pages/FeaturedItems.vue';
import PublishedItems from './pages/PublishedItems.vue';
import Item from './pages/Item.vue';
import Seller from './pages/Seller.vue';
import SellerItems from './pages/SellerItems.vue';
import Publish from './pages/Publish.vue';
import Buying from './pages/Buying.vue';
import Selling from './pages/Selling.vue';
import Messages from './pages/Messages.vue';
import NotFound from './pages/NotFound.vue';
function get_route_map() {
const read_only_routes = {
'marketplace/home': Home,
'marketplace/search/:category/:keyword': Search,
'marketplace/category/:category': Category,
'marketplace/item/:item': Item,
'marketplace/seller/:seller': Seller,
'marketplace/seller/:seller/items': SellerItems,
'marketplace/not-found': NotFound,
}
const registered_routes = {
'marketplace/profile': Seller,
'marketplace/saved-items': SavedItems,
'marketplace/featured-items': FeaturedItems,
'marketplace/publish': Publish,
'marketplace/published-items': PublishedItems,
'marketplace/buying': Buying,
'marketplace/buying/:item': Messages,
'marketplace/selling': Selling,
'marketplace/selling/:buyer/:item': Messages
}
return hub.is_seller_registered()
? Object.assign({}, read_only_routes, registered_routes)
: read_only_routes;
}
export default {
data() {
return {
current_page: this.get_current_page()
}
},
mounted() {
frappe.route.on('change', () => {
if (frappe.get_route()[0] === 'marketplace') {
this.set_current_page();
frappe.utils.scroll_to(0);
}
});
},
methods: {
set_current_page() {
this.current_page = this.get_current_page();
},
get_current_page() {
const route_map = get_route_map();
const curr_route = frappe.get_route_str();
let route = Object.keys(route_map).filter(route => route == curr_route)[0];
if (!route) {
// find route by matching it with dynamic part
const curr_route_parts = curr_route.split('/');
const weighted_routes = Object.keys(route_map)
.map(route_str => route_str.split('/'))
.filter(route_parts => route_parts.length === curr_route_parts.length)
.reduce((obj, route_parts) => {
const key = route_parts.join('/');
let weight = 0;
route_parts.forEach((part, i) => {
const curr_route_part = curr_route_parts[i];
if (part === curr_route_part || part.includes(':')) {
weight += 1;
}
});
obj[key] = weight;
return obj;
}, {});
// get the route with the highest weight
for (let key in weighted_routes) {
const route_weight = weighted_routes[key];
if (route_weight === curr_route_parts.length) {
route = key;
break;
} else {
route = null;
}
}
}
if (!route) {
return {
key: 'not-found',
component: NotFound
};
}
return {
key: curr_route,
component: route_map[route]
}
}
}
}
</script>

View File

@ -1,110 +0,0 @@
<template>
<div ref="sidebar-container">
<ul class="list-unstyled hub-sidebar-group" data-nav-buttons>
<li class="hub-sidebar-item" v-for="item in items" :key="item.label" v-route="item.route" v-show="item.condition === undefined || item.condition()">
{{ item.label }}
</li>
</ul>
<ul class="list-unstyled hub-sidebar-group" data-categories>
<li class="hub-sidebar-item is-title bold text-muted">
{{ __('Categories') }}
</li>
<li class="hub-sidebar-item" v-for="category in categories" :key="category.label" v-route="category.route">
{{ category.label }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
hub_registered: hub.is_user_registered(),
items: [
{
label: __('Browse'),
route: 'marketplace/home'
},
{
label: __('Saved Items'),
route: 'marketplace/saved-items',
condition: () => this.hub_registered
},
{
label: __('Your Featured Items'),
route: 'marketplace/featured-items',
condition: () => this.hub_registered
},
{
label: __('Your Profile'),
route: 'marketplace/profile',
condition: () => this.hub_registered
},
{
label: __('Your Items'),
route: 'marketplace/published-items',
condition: () => this.hub_registered
},
{
label: __('Publish Items'),
route: 'marketplace/publish',
condition: () => this.hub_registered
},
{
label: __('Selling'),
route: 'marketplace/selling',
condition: () => this.hub_registered
},
{
label: __('Buying'),
route: 'marketplace/buying',
condition: () => this.hub_registered
},
],
categories: []
}
},
created() {
this.get_categories()
.then(categories => {
this.categories = categories.map(c => {
return {
label: __(c.name),
route: 'marketplace/category/' + c.name
}
});
this.categories.unshift({
label: __('All'),
route: 'marketplace/home'
});
this.$nextTick(() => {
this.update_sidebar_state();
});
});
erpnext.hub.on('seller-registered', () => {
this.hub_registered = true;
})
},
mounted() {
this.update_sidebar_state();
frappe.route.on('change', () => this.update_sidebar_state());
},
methods: {
get_categories() {
return hub.call('get_categories');
},
update_sidebar_state() {
const container = $(this.$refs['sidebar-container']);
const route = frappe.get_route();
const route_str = route.join('/');
const part_route_str = route.slice(0, 2).join('/');
const $sidebar_item = container.find(`[data-route="${route_str}"], [data-route="${part_route_str}"]`);
const $siblings = container.find('[data-route]');
$siblings.removeClass('active').addClass('text-muted');
$sidebar_item.addClass('active').removeClass('text-muted');
},
}
}
</script>

View File

@ -1,39 +0,0 @@
<template>
<div>
<div ref="comment-input"></div>
<div class="level">
<div class="level-left">
<span class="text-muted">{{ __('Ctrl + Enter to submit') }}</span>
</div>
<div class="level-right">
<button class="btn btn-primary btn-xs" @click="submit_input">{{ __('Submit') }}</button>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
this.make_input();
},
methods: {
make_input() {
this.message_input = frappe.ui.form.make_control({
parent: this.$refs['comment-input'],
on_submit: (message) => {
this.message_input.reset();
this.$emit('change', message);
},
only_input: true,
no_wrapper: true
});
},
submit_input() {
if (!this.message_input) return;
const value = this.message_input.get_value();
if (!value) return;
this.message_input.submit();
}
}
}
</script>

View File

@ -1,26 +0,0 @@
<template>
<p class="text-muted" v-if="!Array.isArray(this.header_items)" v-html="header_items"></p>
<p class="text-muted" v-else>
<span v-for="(header_item , index) in header_items" :key="index">
<span v-if="index" v-html="spacer"></span>
<span v-if="typeof(header_item) == 'string'" v-html="header_item"></span>
<a v-else-if="typeof(header_item) == 'object'" @click="header_item.on_click(header_item.value)" v-html="header_item.value"></a>
</span>
</p>
</template>
<script>
const spacer = '<span aria-hidden="true"> · </span>';
export default {
name: 'detail-header-item',
props: ['value'],
data() {
return {
header_items: this.value,
spacer: spacer
}
},
}
</script>

View File

@ -1,86 +0,0 @@
<template>
<div class="hub-item-container">
<div class="row visible-xs">
<div class="col-xs-12 margin-bottom">
<button class="btn btn-xs btn-default" data-route="marketplace/home">{{ back_to_home_text }}</button>
</div>
</div>
<div v-if="show_skeleton" class="row margin-bottom">
<div class="col-md-3">
<div class="hub-item-skeleton-image"></div>
</div>
<div class="col-md-6">
<h2 class="hub-skeleton" style="width: 75%;">Name</h2>
<div class="text-muted">
<p class="hub-skeleton" style="width: 35%;">Details</p>
<p class="hub-skeleton" style="width: 50%;">Ratings</p>
</div>
<hr>
<div class="hub-item-description">
<p class="hub-skeleton">Desc</p>
<p class="hub-skeleton" style="width: 85%;">Desc</p>
</div>
</div>
</div>
<div v-else>
<div class="row margin-bottom">
<div class="col-md-3">
<div class="hub-item-image">
<base-image :src="image" :alt="title" />
</div>
</div>
<div class="col-md-8" style='padding-left: 30px;'>
<h2>{{ title }}</h2>
<div class="text-muted">
<slot name="detail-header-item"></slot>
</div>
</div>
<div v-if="menu_items && menu_items.length" class="col-md-1">
<div class="dropdown pull-right hub-item-dropdown">
<a class="dropdown-toggle btn btn-xs btn-default" data-toggle="dropdown">
<span class="caret"></span>
</a>
<ul class="dropdown-menu dropdown-right" role="menu">
<li v-for="menu_item in menu_items"
v-if="menu_item.condition"
:key="menu_item.label"
>
<a @click="menu_item.action">{{ menu_item.label }}</a>
</li>
</ul>
</div>
</div>
</div>
<div v-for="section in sections" class="row hub-item-description margin-bottom"
:key="section.title"
>
<h6 class="col-md-12 margin-top">
<b class="text-muted">{{ section.title }}</b>
</h6>
<p class="col-md-12" v-html="section.content">
</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'detail-view',
props: ['title', 'image', 'sections', 'show_skeleton', 'menu_items'],
data() {
return {
back_to_home_text: __('Back to Home')
}
},
computed: {}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -1,50 +0,0 @@
<template>
<div class="empty-state flex flex-column"
:class="{ 'bordered': bordered, 'align-center': centered, 'justify-center': centered }"
:style="{ height: height + 'px' }"
>
<p class="text-muted" v-html="message" ></p>
<p v-if="action">
<button class="btn btn-default btn-xs"
@click="action.on_click"
>
{{ action.label }}
</button>
</p>
</div>
</template>
<script>
export default {
name: 'empty-state',
props: {
message: String,
bordered: Boolean,
height: Number,
action: Object,
centered: {
type: Boolean,
default: true
}
}
}
</script>
<style lang="less">
@import "../../../../../../frappe/frappe/public/less/variables.less";
.empty-state {
height: 500px;
}
.empty-state.bordered {
border-radius: 4px;
border: 1px solid @border-color;
border-style: dashed;
// bad, due to item card column layout, that is inner 15px margin
margin: 0 15px;
}
</style>

View File

@ -1,40 +0,0 @@
<template>
<div class="hub-image">
<img :src="src" :alt="alt" v-show="!is_loading && !is_broken"/>
<div class="hub-image-loading" v-if="is_loading">
<span class="octicon octicon-cloud-download"></span>
</div>
<div class="hub-image-broken" v-if="is_broken">
<span class="octicon octicon-file-media"></span>
</div>
</div>
</template>
<script>
export default {
name: 'Image',
props: ['src', 'alt'],
data() {
return {
is_loading: true,
is_broken: false
}
},
created() {
this.handle_image();
},
methods: {
handle_image() {
let img = new Image();
img.src = this.src;
img.onload = () => {
this.is_loading = false;
};
img.onerror = () => {
this.is_loading = false;
this.is_broken = true;
};
}
}
};
</script>

View File

@ -1,142 +0,0 @@
<template>
<div v-if="seen" class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
<div class="hub-card"
@click="on_click(item_id)"
>
<div class="hub-card-header flex justify-between">
<div class="ellipsis" :style="{ width: '85%' }">
<div class="hub-card-title ellipsis bold">{{ title }}</div>
<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
</div>
<i v-if="allow_clear"
class="octicon octicon-x text-extra-muted"
@click.stop="$emit('remove-item', item_id)"
>
</i>
</div>
<div class="hub-card-body">
<base-image class="hub-card-image" :src="item.image" :alt="title" />
<div class="hub-card-overlay">
<div v-if="is_local" class="hub-card-overlay-body">
<div class="hub-card-overlay-button">
<button class="btn btn-default zoom-view">
<i class="octicon octicon-pencil text-muted"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'item-card',
props: ['item', 'item_id_fieldname', 'is_local', 'on_click', 'allow_clear', 'seen'],
computed: {
title() {
const item_name = this.item.item_name || this.item.name;
return strip_html(item_name);
},
subtitle() {
const dot_spacer = '<span aria-hidden="true"> · </span>';
if(this.is_local){
return comment_when(this.item.creation);
} else {
let subtitle_items = [comment_when(this.item.creation)];
const rating = this.item.average_rating;
if (rating > 0) {
subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
}
subtitle_items.push(this.item.company);
return subtitle_items.join(dot_spacer);
}
},
item_id() {
return this.item[this.item_id_fieldname];
}
}
}
</script>
<style lang="less" scoped>
@import "../../../../../../frappe/frappe/public/less/variables.less";
.hub-card {
margin-bottom: 25px;
position: relative;
border: 1px solid @border-color;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
&:hover .hub-card-overlay {
display: block;
}
.octicon-x {
display: block;
font-size: 20px;
margin-left: 10px;
cursor: pointer;
}
}
.hub-card.closable {
.octicon-x {
display: block;
}
}
.hub-card.is-local {
&.active {
.hub-card-header {
background-color: #f4ffe5;
}
}
}
.hub-card-header {
position: relative;
padding: 12px 15px;
height: 60px;
border-bottom: 1px solid @border-color;
}
.hub-card-body {
position: relative;
height: 200px;
}
.hub-card-overlay {
display: none;
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.05);
}
.hub-card-overlay-body {
position: relative;
height: 100%;
}
.hub-card-overlay-button {
position: absolute;
right: 15px;
bottom: 15px;
}
.hub-card-image {
width: 100%;
height: 100%;
object-fit: contain;
}
</style>

View File

@ -1,62 +0,0 @@
<template>
<div class="item-cards-container">
<empty-state
v-if="items.length === 0"
:message="empty_state_message"
:action="empty_state_action"
:bordered="true"
:height="empty_state_height"
/>
<item-card
v-for="item in items"
:key="container_name + '_' +item[item_id_fieldname]"
:item="item"
:item_id_fieldname="item_id_fieldname"
:is_local="is_local"
:on_click="on_click"
:allow_clear="editable"
:seen="item.hasOwnProperty('seen') ? item.seen : true"
@remove-item="$emit('remove-item', item[item_id_fieldname])"
>
</item-card>
</div>
</template>
<script>
import ItemCard from './ItemCard.vue';
import EmptyState from './EmptyState.vue';
export default {
name: 'item-cards-container',
props: {
container_name: String,
items: Array,
item_id_fieldname: String,
is_local: Boolean,
on_click: Function,
editable: Boolean,
empty_state_message: String,
empty_state_action: Object,
empty_state_height: Number,
empty_state_bordered: Boolean
},
components: {
ItemCard,
EmptyState
},
watch: {
items() {
// TODO: handling doesn't work
frappe.dom.handle_broken_images($(this.$el));
}
}
}
</script>
<style scoped>
.item-cards-container {
margin: 0 -15px;
overflow: overlay;
}
</style>

View File

@ -1,21 +0,0 @@
<template>
<div class="hub-list-item" :data-route="item.route">
<div class="hub-list-left">
<base-image class="hub-list-image" :src="item.image" />
<div class="hub-list-body ellipsis">
<div class="hub-list-title">{{item.item_name}}</div>
<div class="hub-list-subtitle ellipsis">
<slot name="subtitle"></slot>
</div>
</div>
</div>
<div class="hub-list-right" v-if="message">
<span class="text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" />
</div>
</div>
</template>
<script>
export default {
props: ['item', 'message']
}
</script>

View File

@ -1,38 +0,0 @@
<template>
<div v-if="message" class="subpage-message">
<p class="text-muted flex">
<span v-html="message"></span>
<i class="octicon octicon-x text-extra-muted"
@click="$emit('remove-message')"
>
</i>
</p>
</div>
</template>
<script>
export default {
name: 'notification-message',
props: {
message: String,
}
}
</script>
<style lang="less" scoped>
.subpage-message {
p {
padding: 10px 15px;
margin-top: 0px;
margin-bottom: 15px;
background-color: #f9fbf7;
border-radius: 4px;
justify-content: space-between;
}
.octicon-x {
cursor: pointer;
}
}
</style>

View File

@ -1,16 +0,0 @@
<template>
<span>
<i v-for="index in max_rating"
:key="index"
class="fa fa-fw star-icon"
:class="{'fa-star': index <= rating, 'fa-star-o': index > rating}"
>
</i>
</span>
</template>
<script>
export default {
props: ['rating', 'max_rating']
}
</script>

View File

@ -1,140 +0,0 @@
<template>
<div>
<div class="timeline-head">
<div class="comment-input-wrapper">
<div class="comment-input-header">
<span class="text-muted">{{ __('Add your review') }}</span>
<div class="btn btn-default btn-xs pull-right"
@click="on_submit_review"
:disabled="!(user_review.rating && user_review.subject)"
>
{{ __('Submit Review') }}
</div>
</div>
<div class="comment-input-container">
<div class="rating-area text-muted">
<span>{{ __('Your rating:') }}</span>
<div
v-for="i in [1, 2, 3, 4, 5]"
:key="i"
:class="['fa fa-fw', user_review.rating < i ? 'fa-star-o' : 'fa-star']"
:data-index="i"
@click="set_rating(i)"
>
</div>
</div>
<div class="comment-input-body margin-top" v-show="user_review.rating">
<input
type="text"
placeholder="Subject"
class="form-control margin-bottom"
style="border-color: #ebeff2"
v-model="user_review.subject"
>
<div ref="review-content"></div>
<div>
<span class="text-muted text-small">{{ __('Ctrl+Enter to submit') }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="timeline-items">
<review-timeline-item v-for="review in reviews"
:key="review.user"
:username="review.username"
:avatar="review.user_image"
:comment_when="when(review.modified)"
:rating="review.rating"
:subject="review.subject"
:content="review.content"
>
</review-timeline-item>
</div>
</div>
</template>
<script>
import ReviewTimelineItem from '../components/ReviewTimelineItem.vue';
export default {
props: ['hub_item_name'],
data() {
return {
user_review: {
rating: 0,
subject: '',
content: ''
},
reviews: []
}
},
components: {
ReviewTimelineItem
},
created() {
this.get_item_reviews();
},
mounted() {
this.make_input();
},
methods: {
set_rating(i) {
this.user_review.rating = i;
},
when(datetime) {
return comment_when(datetime);
},
get_item_reviews() {
hub.call('get_item_reviews', { hub_item_name: this.hub_item_name })
.then(reviews => {
this.reviews = reviews;
})
.catch(() => {});
},
make_input() {
this.review_content = frappe.ui.form.make_control({
parent: this.$refs['review-content'],
on_submit: this.on_submit_review.bind(this),
no_wrapper: true,
only_input: true,
render_input: true,
df: {
fieldtype: 'Comment',
fieldname: 'comment'
}
});
},
on_submit_review() {
const review = Object.assign({}, this.user_review, {
content: this.review_content.get_value()
});
if (!hub.is_seller_registered()) {
frappe.throw(__('You need to login as a Marketplace User before you can add any reviews.'));
}
hub.call('add_item_review', {
hub_item_name: this.hub_item_name,
review: JSON.stringify(review)
})
.then(this.push_review.bind(this));
this.reset_user_review();
},
reset_user_review() {
this.user_review.rating = 0;
this.user_review.subject = '';
this.review_content.set_value('');
},
push_review(review){
this.reviews.unshift(review);
}
}
}
</script>

View File

@ -1,53 +0,0 @@
<template>
<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
<!-- ${image_html} -->
</span>
<div class="pull-left media-body">
<div class="media-content-wrapper">
<div class="action-btns">
<!-- ${edit_html} -->
</div>
<div class="comment-header clearfix">
<span class="pull-left avatar avatar-small visible-xs">
<!-- ${image_html} -->
</span>
<div class="asset-details">
<span class="author-wrap">
<i class="octicon octicon-quote hidden-xs fa-fw"></i>
<span>
{{ username }}
</span>
</span>
<a class="text-muted">
<span class="text-muted hidden-xs">&ndash;</span>
<span class="hidden-xs" v-html="comment_when"></span>
</a>
</div>
</div>
<div class="reply timeline-content-show">
<div class="timeline-item-content">
<p class="text-muted">
<rating :rating="rating" :max_rating="5"></rating>
</p>
<h6 class="bold">{{ subject }}</h6>
<p class="text-muted" v-html="content"></p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Rating from '../components/Rating.vue';
export default {
props: ['username', 'comment_when', 'avatar', 'rating', 'subject', 'content'],
components: {
Rating
}
}
</script>

View File

@ -1,26 +0,0 @@
<template>
<div class="hub-search-container">
<input
type="text"
class="form-control"
:placeholder="placeholder"
:value="value"
@keydown.enter="on_input">
</div>
</template>
<script>
export default {
props: {
placeholder: String,
value: String,
on_search: Function
},
methods: {
on_input(event) {
this.$emit('input', event.target.value);
this.on_search();
}
}
};
</script>

View File

@ -1,3 +0,0 @@
<template>
<div class="hub-items-header level"><slot></slot></div>
</template>

View File

@ -1,9 +0,0 @@
/* Saving this for later */
<template>
<div class="media timeline-item notification-content">
<div class="small">
<i class="octicon octicon-bookmark fa-fw"></i>
<span title="Administrator"><b>4 weeks ago</b> Published 1 item to Marketplace</span>
</div>
</div>
</template>

View File

@ -1,41 +0,0 @@
function edit_details_dialog(params) {
let dialog = new frappe.ui.Dialog({
title: __('Update Details'),
fields: [
{
label: 'Item Name',
fieldname: 'item_name',
fieldtype: 'Data',
default: params.defaults.item_name,
reqd: 1
},
{
label: 'Hub Category',
fieldname: 'hub_category',
fieldtype: 'Autocomplete',
default: params.defaults.hub_category,
options: [],
reqd: 1
},
{
label: 'Description',
fieldname: 'description',
fieldtype: 'Text',
default: params.defaults.description,
options: [],
reqd: 1
}
],
primary_action_label: params.primary_action.label || __('Update Details'),
primary_action: params.primary_action.fn
});
hub.call('get_categories').then(categories => {
categories = categories.map(d => d.name);
dialog.fields_dict.hub_category.set_data(categories);
});
return dialog;
}
export { edit_details_dialog };

View File

@ -1,39 +0,0 @@
function ItemPublishDialog(primary_action, secondary_action) {
let dialog = new frappe.ui.Dialog({
title: __('Edit Publishing Details'),
fields: [
{
label: __('Item Code'),
fieldname: 'item_code',
fieldtype: 'Data',
read_only: 1
},
{
label: __('Hub Category'),
fieldname: 'hub_category',
fieldtype: 'Autocomplete',
options: [],
reqd: 1
},
{
label: __('Images'),
fieldname: 'image_list',
fieldtype: 'MultiSelect',
options: [],
reqd: 1
}
],
primary_action_label: primary_action.label || __('Set Details'),
primary_action: primary_action.fn,
secondary_action: secondary_action.fn
});
hub.call('get_categories').then(categories => {
categories = categories.map(d => d.name);
dialog.fields_dict.hub_category.set_data(categories);
});
return dialog;
}
export { ItemPublishDialog };

View File

@ -1,56 +0,0 @@
const ProfileDialog = (title = __('Edit Profile'), action={}) => {
const fields = [
{
fieldtype: 'Link',
fieldname: 'company',
label: __('Company'),
options: 'Company'
},
{
fieldtype: 'Read Only',
fieldname: 'email',
label: __('Email')
},
{
label: __('About your company'),
fieldname: 'company_description',
fieldtype: 'Text'
}
];
let dialog = new frappe.ui.Dialog({
title: title,
fields: fields,
primary_action_label: action.label || __('Update'),
primary_action: () => {
const form_values = dialog.get_values();
let values_filled = true;
// TODO: Say "we notice that the company description and logo isn't set. Please set them in master."
// Only then allow to register
const mandatory_fields = ['company', 'company_description'];
mandatory_fields.forEach(field => {
const value = form_values[field];
if (!value) {
dialog.set_df_property(field, 'reqd', 1);
values_filled = false;
}
});
if (!values_filled) return;
action.on_submit(form_values);
}
});
// Post create
const default_company = frappe.defaults.get_default('company');
dialog.set_value('company', default_company);
dialog.set_value('email', frappe.session.user);
return dialog;
}
export {
ProfileDialog
}

View File

@ -1,80 +0,0 @@
function get_review_html(review) {
let username = review.username || review.user || __("Anonymous");
let image_html = review.user_image
? `<div class="avatar-frame" style="background-image: url(${review.user_image})"></div>`
: `<div class="standard-image" style="background-color: #fafbfc">${frappe.get_abbr(username)}</div>`
let edit_html = review.own
? `<div class="pull-right hidden-xs close-btn-container">
<span class="small text-muted">
${'data.delete'}
</span>
</div>
<div class="pull-right edit-btn-container">
<span class="small text-muted">
${'data.edit'}
</span>
</div>`
: '';
let rating_html = get_rating_html(review.rating);
return get_timeline_item(review, image_html, edit_html, rating_html);
}
function get_timeline_item(data, image_html, edit_html, rating_html) {
return `<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
${image_html}
</span>
<div class="pull-left media-body">
<div class="media-content-wrapper">
<div class="action-btns">${edit_html}</div>
<div class="comment-header clearfix">
<span class="pull-left avatar avatar-small visible-xs">
${image_html}
</span>
<div class="asset-details">
<span class="author-wrap">
<i class="octicon octicon-quote hidden-xs fa-fw"></i>
<span>${data.username}</span>
</span>
<a class="text-muted">
<span class="text-muted hidden-xs">&ndash;</span>
<span class="hidden-xs">${comment_when(data.modified)}</span>
</a>
</div>
</div>
<div class="reply timeline-content-show">
<div class="timeline-item-content">
<p class="text-muted">
${rating_html}
</p>
<h6 class="bold">${data.subject}</h6>
<p class="text-muted">
${data.content}
</p>
</div>
</div>
</div>
</div>
</div>`;
}
function get_rating_html(rating) {
let rating_html = ``;
for (var i = 0; i < 5; i++) {
let star_class = 'fa-star';
if (i >= rating) star_class = 'fa-star-o';
rating_html += `<i class='fa fa-fw ${star_class} star-icon' data-index=${i}></i>`;
}
return rating_html;
}
export {
get_review_html,
get_rating_html
}

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