[added] hospitality domain (#11020)

* [added] hospitality domain

* [tests] wip

* [tests] for restaurant

* [fix] tests for new naming

* [docs] added restaurant docs

* [docs] added restaurant docs
This commit is contained in:
Rushabh Mehta 2017-10-17 12:30:34 +05:30 committed by GitHub
parent bfb108d722
commit bc4e2cd9c1
87 changed files with 2851 additions and 362 deletions

View File

@ -1,3 +1,5 @@
# coding=utf-8
from __future__ import unicode_literals
from frappe import _
@ -284,4 +286,12 @@ def get_data():
"link": "data-import-tool",
"label": _("Data Import Tool")
},
{
"module_name": "Restaurant",
"color": "#EA81E8",
"icon": "🍔",
"_doctype": "Restaurant",
"link": "List/Restaurant",
"label": _("Restaurant")
}
]

View File

@ -83,6 +83,9 @@ class AccountsController(TransactionBase):
self.set(fieldname, today())
break
# set taxes table if missing from `taxes_and_charges`
self.set_taxes()
def calculate_taxes_and_totals(self):
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
calculate_taxes_and_totals(self)
@ -259,9 +262,9 @@ class AccountsController(TransactionBase):
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if gl_dict.account and self.doctype not in ["Journal Entry",
if gl_dict.account and self.doctype not in ["Journal Entry",
"Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)

View File

@ -165,6 +165,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
and (tabItem.`{key}` LIKE %(txt)s
or tabItem.item_group LIKE %(txt)s
or tabItem.item_name LIKE %(txt)s
or tabItem.barcode LIKE %(txt)s
or tabItem.description LIKE %(txt)s)
{fcond} {mcond}
order by
@ -172,7 +173,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
if(locate(%(_txt)s, item_name), locate(%(_txt)s, item_name), 99999),
idx desc,
name, item_name
limit %(start)s, %(page_len)s """.format(key=searchfield,
limit %(start)s, %(page_len)s """.format(
key=searchfield,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%')),
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

View File

@ -0,0 +1,9 @@
# Hospitality
ERPNext Hospitality module is designed to handle workflows for Hotels and Restaurants. This is still in early development stage.
### Manage Restaurants
The Restaurant module in ERPNext will help you manage a chain of restaurants. You can create Restaurants, Menus, Tables, Reservations and a manage Order Entry and Billing.
{index}

View File

@ -0,0 +1,4 @@
restaurant
restaurant-menu
reservations
order-entry

View File

@ -0,0 +1,26 @@
# Restaurant Order Entry
The Restaurant Order Entry is the screen where the waiters will punch in orders related to a particular table.
This screen makes it easy for the waiters in your restaurant to punch in orders from various tables.
When the guest places an order, the waiter will select the table number and add the items in the Order Entry. This can be changed until it is time for the bill. Unless you bill a table, you can change the items and they will automatically appear when you select the table ID.
To place an order you can select an item and click the enter key so that the item will be updated in the items table.
<img class="screenshot" alt="Order Entry" src="/docs/assets/img/restaurant/order-entry.png">
You can also choose items with the POS style item selector.
### Billing
When it is time for billing, you just choose the bill and you can select the customer and mode of payment. On saving, a Sales Invoice is generated and the order section becomes empty.
<img class="screenshot" alt="Order Entry" src="/docs/assets/img/restaurant/order-entry-bill.png">
### Sales Invoice
To print the invoice, you can click on the Invoice Link and print the invoice
<img class="screenshot" alt="Sales Invoice" src="/docs/assets/img/restaurant/restaurant-invoice.png">

View File

@ -0,0 +1,13 @@
# Restaurant Reservations
Once you have setup the restaurant and tables, you can start taking in reservations for your restaurant.
To take a reservation, just make a new Restaurant Reservation from the Restaurant Page and set the time, number of people and name of the guest.
<img class="screenshot" alt="Reservation" src="/docs/assets/img/restaurant/reservation.png">
### Kanban
As your guests walk in, You can also manage the reservations by making a simple Kanban board for the same.
<img class="screenshot" alt="Reservation Kanban Board" src="/docs/assets/img/restaurant/reservation-kanban.png">

View File

@ -0,0 +1,7 @@
# Restaurant Menu
For every restaurant you must set an active Restaurant Menu from which orders can be placed. You can also set the rates for each of the item for the day.
When you save the Restaurant Menu, a Price List is created for that Menu and all pricing is linked to that price list. This way you can easily control the items on offer and pricing from the menu.
<img class="screenshot" alt="Restaurant Menu" src="/docs/assets/img/restaurant/restaurant-menu.png">

View File

@ -0,0 +1,19 @@
# Restaurant
The Restaurant record represents one restaurant in your organization. To create a new Restaurant, just set the name, Company and Default Customer.
You can set a unique numbering prefix for each of your restaurants. All invoices for that restuarant will follow that numbering prefix.
If you have a default Sales Taxes and Charges Template, you can add it so that the same charge + tax will be applicable for all invoices in the restaurant.
<img class="screenshot" alt="Restaurant" src="/docs/assets/img/restaurant/restaurant.png">
After your restaurant is created, you can add Tables and Menus for that restaurant
### Adding Tables
You can add a Restaurant Table by creating a new Restaurant Table from the dashboard.
<img class="screenshot" alt="Restaurant Table" src="/docs/assets/img/restaurant/restaurant-table.png">

View File

@ -97,8 +97,8 @@ fiscal year.
A Cost Center is like an Account, but the only difference is that its
structure represents your business more closely than Accounts.
For example, in your Chart of Accounts, you can separate your expenses by its type
(i.e., travel, marketing, etc.). In your Chart of Cost Centers, you can separate
For example, in your Chart of Accounts, you can separate your expenses by its type
(i.e., travel, marketing, etc.). In your Chart of Cost Centers, you can separate
them by product line or business group (e.g., online sales, retail sales, etc.).
> Accounts > Chart of Cost Centers
@ -316,7 +316,7 @@ A record of the monthly salary given to an Employee.
#### Salary Structure
A template identifying all the components of an Employees' salary (earnings),
A template identifying all the components of an Employees' salary (earnings),
tax and other social security deductions.
> Human Resource > Salary and Payroll > Salary Structure

View File

@ -1,7 +1,3 @@
# Getting Started With Erpnext
<!-- Getting Started with ERPNext-->
# Getting Started with ERPNext
There are many ways to get started with ERPNext.

View File

@ -1,9 +1,5 @@
# The Champion
<!-- no-heading -->
<h1 class="white">The Champion</h1>
<img alt="Champion" class="screenshot" src="/docs/assets/img/setup/implementation-image.png">
We have seen dozens of ERP implementations over the past few years and we

View File

@ -1,22 +0,0 @@
If you have a contract with the Customer where your organization gives bill to the Customer on a monthly, quarterly, half-yearly or annual basis, you can use subscription feature to make auto invoicing.
<img class="screenshot" alt="Subscription" src="{{docs_base_url}}/assets/img/subscription/subscription.png">
#### Scenario
Subscription for your hosted ERPNext account requires yearly renewal. We use Sales Invoice for generating proforma invoices. To automate proforma invoicing for renewal, we set original Sales Invoice on the subscription form. Recurring proforma invoice is created automatically just before customer's account is about to expire, and requires renewal. This recurring Proforma Invoice is also emailed automatically to the customer.
To set the subscription for the sales invoice
Goto Subscription > select base doctype "Sales Invoice" > select base docname "Invoice No" > Save
<img class="screenshot" alt="Subscription" src="{{docs_base_url}}/assets/img/subscription/subscription.gif">
**From Date and To Date**: This defines contract period with the customer.
**Repeat on Day**: If frequency is set as Monthly, then it will be day of the month on which recurring invoice will be generated.
**Notify By Email**: If you want to notify the user about auto recurring invoice.
**Print Format**: Select a print format to define document view which should be emailed to customer.
**Disabled**: It will stop to make auto recurring documents against the subscription

View File

@ -0,0 +1,18 @@
data = {
'desktop_icons': [
'Item',
'Customer',
'Supplier',
'Lead',
'Sales Order',
'Purchase Order',
'Task',
'Sales Invoice',
'CRM',
'ToDo'
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
}

View File

@ -0,0 +1,37 @@
data = {
'desktop_icons': [
'Student',
'Program',
'Course',
'Student Group',
'Instructor',
'Fees',
'Task',
'ToDo',
'Schools'
],
'default_portal_role': 'Student',
'restricted_roles': [
'Student',
'Instructor',
'Academics User'
],
'modules': [
'Schools'
],
'fixtures': [
dict(doctype='Academic Year', academic_year_name='2013-14'),
dict(doctype='Academic Year', academic_year_name='2014-15'),
dict(doctype='Academic Year', academic_year_name='2015-16'),
dict(doctype='Academic Year', academic_year_name='2016-17'),
dict(doctype='Academic Year', academic_year_name='2017-18'),
dict(doctype='Academic Year', academic_year_name='2018-19'),
dict(doctype='Academic Year', academic_year_name='2019-20'),
dict(doctype='Academic Term', academic_year='2016-17', term_name='Semester 1'),
dict(doctype='Academic Term', academic_year='2016-17', term_name='Semester 2'),
dict(doctype='Academic Term', academic_year='2016-17', term_name='Semester 3'),
dict(doctype='Academic Term', academic_year='2017-18', term_name='Semester 1'),
dict(doctype='Academic Term', academic_year='2017-18', term_name='Semester 2'),
dict(doctype='Academic Term', academic_year='2017-18', term_name='Semester 3')
]
}

View File

@ -0,0 +1,29 @@
data = {
'desktop_icons': [
'Patient',
'Patient Appointment',
'Consultation',
'Lab Test',
'Healthcare',
'Accounts',
'Buying',
'Stock',
'HR',
'ToDo'
],
'default_portal_role': 'Patient',
'restricted_roles': [
'Healthcare Administrator',
'LabTest Approver',
'Laboratory User',
'Nursing User',
'Physician',
'Patient'
],
'custom_fields': {
'Sales Invoice': dict(fieldname='appointment', label='Patient Appointment',
fieldtype='Link', options='Patient Appointment',
insert_after='customer')
},
'on_setup': 'erpnext.healthcare.setup.setup_healthcare'
}

View File

@ -0,0 +1,32 @@
data = {
'desktop_icons': [
'Restaurant',
'Accounts',
'Buying',
'Stock',
'HR',
'Project',
'ToDo'
],
'restricted_roles': [
'Restaurant Manager'
],
'custom_fields': {
'Sales Invoice': [
{
'fieldname': 'restaurant', 'fieldtype': 'Link', 'options': 'Restaurant',
'insert_after': 'customer_name', 'label': 'Restaurant',
},
{
'fieldname': 'restaurant_table', 'fieldtype': 'Link', 'options': 'Restaurant Table',
'insert_after': 'restaurant', 'label': 'Restaurant Table',
}
],
'Price List': [
{
'fieldname':'restaurant_menu', 'fieldtype':'Link', 'options':'Restaurant Menu', 'label':'Restaurant Menu',
'insert_after':'currency'
}
]
}
}

View File

@ -0,0 +1,25 @@
data = {
'desktop_icons': [
'Item',
'BOM',
'Customer',
'Supplier',
'Sales Order',
'Purchase Order',
'Production Order',
'Task',
'Accounts',
'HR',
'ToDo'
],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'collapsible_depends_on', 'value': 'is_stock_item'},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'restricted_roles': [
'Manufacturing User'
],
'default_portal_role': 'Customer'
}

20
erpnext/domains/retail.py Normal file
View File

@ -0,0 +1,20 @@
data = {
'desktop_icons': [
'POS',
'Item',
'Customer',
'Sales Invoice',
'Purchase Order',
'Accounts',
'Task',
'ToDo'
],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1},
{'doctype': 'Customer', 'fieldname': 'credit_limit_section', 'property': 'hidden', 'value': 1},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
}

View File

@ -0,0 +1,22 @@
data = {
'desktop_icons': [
'Project',
'Timesheet',
'Customer',
'Sales Order',
'Sales Invoice',
'CRM',
'Task',
'Expense Claim',
'Employee',
'HR',
'ToDo'
],
'properties': [
{'doctype': 'Item', 'fieldname': 'is_stock_item', 'property': 'default', 'value': 0},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 0]
],
'default_portal_role': 'Customer'
}

View File

@ -1,10 +1,12 @@
from __future__ import unicode_literals
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe import _
def setup_healthcare():
if frappe.db.exists('Medical Department', 'Cardiology'):
# already setup
return
create_medical_departments()
create_antibiotics()
create_test_uom()
@ -14,19 +16,6 @@ def setup_healthcare():
create_lab_test_items()
create_lab_test_template()
create_sensitivity()
make_custom_fields()
def make_custom_fields():
custom_fields = {
'Sales Invoice': [
dict(fieldname='appointment', label='Patient Appointment',
fieldtype='Link', options='Patient Appointment',
insert_after='customer')
]
}
create_custom_fields(custom_fields)
def create_medical_departments():
departments = [

View File

@ -53,6 +53,16 @@ calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Ho
fixtures = ["Web Form"]
domains = {
'Distribution': 'erpnext.domains.distribution',
'Education': 'erpnext.domains.education',
'Healthcare': 'erpnext.domains.healthcare',
'Hospitality': 'erpnext.domains.hospitality',
'Manufacturing': 'erpnext.domains.manufacturing',
'Retail': 'erpnext.domains.retail',
'Services': 'erpnext.domains.services',
}
website_generators = ["Item Group", "Item", "BOM", "Sales Partner",
"Job Opening", "Student Admission"]

View File

@ -13,6 +13,7 @@ QUnit.test("test Salary Structure", function(assert) {
(r) => {
// Creating Salary Structure for employees);
return frappe.tests.make('Salary Structure', [
{ __newname: 'Test Salary Structure'},
{ company: 'For Testing'},
{ payroll_frequency: 'Monthly'},
{ employees: [
@ -47,11 +48,7 @@ QUnit.test("test Salary Structure", function(assert) {
]);
}
),
() => frappe.timeout(18),
() => cur_dialog.set_value('value','Test Salary Structure'),
() => frappe.timeout(1),
() => frappe.click_button('Create'),
() => frappe.timeout(1),
() => frappe.timeout(3),
() => {
// To check if all the fields are correctly set
assert.ok(cur_frm.doc.employees[0].employee_name.includes('Test Employee 1') &&

View File

@ -1,13 +1,6 @@
QUnit.test("test: operation", function (assert) {
assert.expect(2);
let done = assert.async();
let set_op_name = (text) => {
$(`input.input-with-feedback.form-control.bold:visible`).val(`${text}`);
};
let click_create = () => {
$(`.btn-primary:contains("Create"):visible`).click();
};
frappe.run_serially([
// test operation creation
() => frappe.set_route("List", "Operation"),
@ -16,14 +9,11 @@ QUnit.test("test: operation", function (assert) {
() => {
frappe.tests.make(
"Operation", [
{__newname: "Assemble Keyboard"},
{workstation: "Keyboard assembly workstation"}
]
);
},
() => frappe.timeout(4),
() => set_op_name("Assemble Keyboard"),
() => frappe.timeout(0.5),
() => click_create(),
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.docname.includes('Assemble Keyboard'),
@ -36,28 +26,22 @@ QUnit.test("test: operation", function (assert) {
() => {
frappe.tests.make(
"Operation", [
{__newname: 'Assemble Screen'},
{workstation: "Screen assembly workstation"}
]
);
},
() => frappe.timeout(4),
() => set_op_name("Assemble Screen"),
() => frappe.timeout(0.5),
() => click_create(),
() => frappe.timeout(1),
// Create a CPU operation
() => {
frappe.tests.make(
"Operation", [
{__newname: 'Assemble CPU'},
{workstation: "CPU assembly workstation"}
]
);
},
() => frappe.timeout(4),
() => set_op_name("Assemble CPU"),
() => frappe.timeout(0.5),
() => click_create(),
() => frappe.timeout(1),
() => done()

View File

@ -16,3 +16,4 @@ Maintenance
Schools
Regional
Healthcare
Restaurant

View File

@ -4,7 +4,6 @@
from __future__ import unicode_literals
import frappe
from erpnext.setup.setup_wizard import domainify
def execute():
frappe.reload_doctype('Role')
@ -19,6 +18,9 @@ def execute():
frappe.get_doc('Portal Settings').sync_menu()
if 'schools' in frappe.get_installed_apps():
domainify.setup_domain('Education')
domain = frappe.get_doc('Domain', 'Education')
domain.setup_domain()
else:
domainify.setup_sidebar_items(domainify.get_domain('Manufacturing'))
domain = frappe.get_doc('Domain', 'Manufacturing')
domain.setup_data()
domain.setup_sidebar_items()

View File

@ -9,7 +9,7 @@ from frappe.model.mapper import get_mapped_doc
def execute():
# for converting student batch into student group
for doctype in ["Student Group", "Student Group Student",
for doctype in ["Student Group", "Student Group Student", 'Program Enrollment',
"Student Group Instructor", "Student Attendance", "Student", "Student Batch Name"]:
frappe.reload_doc("schools", "doctype", frappe.scrub(doctype))

View File

@ -4,11 +4,6 @@
from __future__ import unicode_literals
import frappe
from erpnext.setup.setup_wizard.domainify import update_module_def_restrict_to_domain
def execute():
""" set the restrict to domain in module def """
frappe.reload_doc("core", "doctype", "module_def")
if frappe.db.get_single_value('System Settings', 'setup_complete'):
update_module_def_restrict_to_domain()
pass

View File

@ -4,5 +4,6 @@ import frappe
from erpnext.setup.install import create_print_zero_amount_taxes_custom_field
def execute():
frappe.reload_doc("printing", "doctype", "print_style")
frappe.reload_doc('printing', 'doctype', 'print_style')
frappe.reload_doc('printing', 'doctype', 'print_settings')
create_print_zero_amount_taxes_custom_field()

View File

@ -1,16 +1,37 @@
<div class="row pos-item-area">
<div class="app-listing item-list image-view-container item-selector">
{% for (var i=0; i < data.length; i++) { var item = data[i]; %}
<div class="col-xs-3 pos-item-wrapper">
<div class="pos-item" data-name="{{ item.name }}">
<div class="pos-item-image"
{% if(item.image) { %}style="background-image: url({{ item.image }});"{% }
else { %}style="background-color: {{ item.color }};"{% } %}>
{% if(!item.image) { %}{{ item.abbr }}{% } %}
{% if (i % 4 === 0) { %}<div class="image-view-row">{% } %}
<div class="image-view-item" data-name="{{ item.name }}">
<div class="image-view-header doclist-row">
<div class="list-value">
<a class="grey list-id" data-name="{{item.name}}"
title="{{ item.item_name || item.name}}">
{{item.item_name || item.name}}</a>
</div>
</div>
<div class="pos-item-text">
<h6 class="item-code ellipsis">{{ item.name }}</h6>
<div class="image-view-body">
<a data-item-code="{{ item.name }}"
title="{{ item.item_name || item.name }}"
>
<div class="image-field"
style="
{% if (!item.image) { %}
background-color: #fafbfc;
{% } %}
border: 0px;"
>
{% if (!item.image) { %}
<span class="placeholder-text">
{%= frappe.get_abbr(item.item_name || item.name) %}
</span>
{% } %}
{% if (item.image) { %}
<img src="{{ item.image }}" alt="{{item.item_name || item.name}}">
{% } %}
</div>
</a>
</div>
</div>
</div>
{% } %}
{% if ((i % 4 === 3) || (i===data.length - 1)) { %}</div>{% } %}
{% endfor %}
</div>

View File

@ -2,6 +2,14 @@ erpnext.ItemSelector = Class.extend({
init: function(opts) {
$.extend(this, opts);
if (!this.item_field) {
this.item_field = 'item_code';
}
if (!this.item_query) {
this.item_query = erpnext.queries.item().query;
}
this.grid = this.frm.get_field("items").grid;
this.setup();
},
@ -32,8 +40,8 @@ erpnext.ItemSelector = Class.extend({
this.dialog.results = body.find('.results');
var me = this;
this.dialog.results.on('click', '.pos-item', function() {
me.add_item($(this).attr('data-name'))
this.dialog.results.on('click', '.image-view-item', function() {
me.add_item($(this).attr('data-name'));
});
this.dialog.input.on('keyup', function() {
@ -52,35 +60,42 @@ erpnext.ItemSelector = Class.extend({
var added = false;
// find row with item if exists
$.each(this.frm.doc.items || [], function(i, d) {
if(d.item_code===item_code) {
$.each(this.frm.doc.items || [], (i, d) => {
if(d[this.item_field]===item_code) {
frappe.model.set_value(d.doctype, d.name, 'qty', d.qty + 1);
frappe.show_alert(__("Added {0} ({1})", [item_code, d.qty]));
frappe.show_alert({message: __("Added {0} ({1})", [item_code, d.qty]), indicator: 'green'});
added = true;
return false;
}
});
if(!added) {
var d = this.grid.add_new_row();
frappe.model.set_value(d.doctype, d.name, 'item_code', item_code);
// after item fetch
frappe.after_ajax(function() {
setTimeout(function() {
var d = null;
frappe.run_serially([
() => { d = this.grid.add_new_row(); },
() => frappe.model.set_value(d.doctype, d.name, this.item_field, item_code),
() => frappe.timeout(0.1),
() => {
frappe.model.set_value(d.doctype, d.name, 'qty', 1);
frappe.show_alert(__("Added {0} ({1})", [item_code, 1]));
}, 100);
});
frappe.show_alert({message: __("Added {0} ({1})", [item_code, 1]), indicator: 'green'});
}
]);
}
},
render_items: function() {
var args = erpnext.queries.item();
let args = {
query: this.item_query,
filters: {}
};
args.txt = this.dialog.input.val();
args.as_dict = 1;
if (this.get_filters) {
$.extend(args.filters, this.get_filters() || {});
}
var me = this;
frappe.link_search("Item", args, function(r) {
$.each(r.values, function(i, d) {
@ -92,4 +107,4 @@ erpnext.ItemSelector = Class.extend({
me.dialog.results.html(frappe.render_template('item_selector', {'data':r.values}));
});
}
})
});

View File

View File

@ -0,0 +1,10 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Restaurant', {
refresh: function(frm) {
frm.add_custom_button(__('Order Entry'), () => {
frappe.set_route('Form', 'Restaurant Order Entry');
});
}
});

View File

@ -0,0 +1,309 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "prompt",
"beta": 0,
"creation": "2017-09-15 12:40:41.546933",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "image",
"fieldtype": "Attach Image",
"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": "Image",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoice_series_prefix",
"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": "Invoice Series Prefix",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "active_menu",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Active Menu",
"length": 0,
"no_copy": 0,
"options": "Restaurant Menu",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_tax_template",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Tax Template",
"length": 0,
"no_copy": 0,
"options": "Sales Taxes and Charges Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Address",
"length": 0,
"no_copy": 0,
"options": "Address",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_field": "image",
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-05 17:41:14.422242",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

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

View File

@ -0,0 +1,16 @@
from frappe import _
def get_data():
return {
'fieldname': 'restaurant',
'transactions': [
{
'label': _('Setup'),
'items': ['Restaurant Menu', 'Restaurant Table']
},
{
'label': _('Operations'),
'items': ['Restaurant Reservation', 'Sales Invoice']
}
]
}

View File

@ -0,0 +1,37 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Restaurant", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(2);
frappe.run_serially([
// insert a new Restaurant
() => {
return frappe.tests.make('Restaurant', [
// values to be set
{__newname: 'Test Restaurant 1'},
{company: 'Test Company'},
{invoice_series_prefix: 'Test-Rest-1-Inv-'}
])
},
() => {
assert.equal(cur_frm.doc.company, 'Test Company');
},
() => {
return frappe.tests.make('Restaurant', [
// values to be set
{__newname: 'Test Restaurant 2'},
{company: 'Test Company'},
{invoice_series_prefix: 'Test-Rest-3-Inv-'}
]);
},
() => {
assert.equal(cur_frm.doc.company, 'Test Company');
},
() => done()
]);
});

View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
test_records = [
dict(doctype='Restaurant', name='Test Restaurant 1', company='_Test Company 1',
invoice_series_prefix='Test-Rest-1-Inv-', defaut_customer='_Test Customer 1'),
dict(doctype='Restaurant', name='Test Restaurant 2', company='_Test Company 1',
invoice_series_prefix='Test-Rest-2-Inv-', defaut_customer='_Test Customer 1'),
]
class TestRestaurant(unittest.TestCase):
pass

View File

@ -0,0 +1,8 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Restaurant Menu', {
setup: function(frm) {
frm.add_fetch('item', 'standard_rate', 'rate');
},
});

View File

@ -0,0 +1,247 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "prompt",
"beta": 0,
"creation": "2017-09-15 12:48:29.818715",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "restaurant",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Restaurant",
"length": 0,
"no_copy": 0,
"options": "Restaurant",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "enabled",
"fieldtype": "Check",
"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": "Enabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "price_list",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Price List (Auto created)",
"length": 0,
"no_copy": 0,
"options": "Price List",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "items_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": "Items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "items",
"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": "Items",
"length": 0,
"no_copy": 0,
"options": "Restaurant Menu Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 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": 0,
"max_attachments": 0,
"modified": "2017-09-21 11:04:20.671542",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Menu",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Restaurant Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class RestaurantMenu(Document):
def validate(self):
for d in self.items:
if not d.rate:
d.rate = frappe.db.get_value('Item', d.item, 'standard_rate')
def on_update(self):
'''Sync Price List'''
self.make_price_list()
def on_trash(self):
'''clear prices'''
self.clear_item_price()
def clear_item_price(self, price_list=None):
'''clear all item prices for this menu'''
if not price_list:
price_list = self.get_price_list().name
frappe.db.sql('delete from `tabItem Price` where price_list = %s', price_list)
def make_price_list(self):
# create price list for menu
price_list = self.get_price_list()
self.db_set('price_list', price_list.name)
# delete old items
self.clear_item_price(price_list.name)
for d in self.items:
frappe.get_doc(dict(
doctype = 'Item Price',
price_list = price_list.name,
item_code = d.item,
price_list_rate = d.rate
)).insert()
def get_price_list(self):
'''Create price list for menu if missing'''
price_list_name = frappe.db.get_value('Price List', dict(restaurant_menu=self.name))
if price_list_name:
price_list = frappe.get_doc('Price List', price_list_name)
else:
price_list = frappe.new_doc('Price List')
price_list.restaurant_menu = self.name
price_list.price_list_name = self.name
price_list.enabled = 1
price_list.selling = 1
price_list.save()
return price_list

View File

@ -0,0 +1,75 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Restaurant Menu", function (assert) {
let done = assert.async();
let items = {
"Food Item 1": [
{item_code: "Food Item 1"},
{item_group: "Products"},
{is_stock_item: 1},
],
"Food Item 2": [
{item_code: "Food Item 2"},
{item_group: "Products"},
{is_stock_item: 1},
],
"Test Product 3": [
{item_code: "Food Item 3"},
{item_group: "Products"},
{is_stock_item: 1},
]
};
// number of asserts
assert.expect(0);
frappe.run_serially([
// insert a new Restaurant Menu
() => frappe.tests.setup_doctype('Item', items),
() => {
return frappe.tests.make("Restaurant Menu", [
{__newname: 'Restaurant Menu 1'},
{restaurant: "Test Restaurant 1"},
{items: [
[
{"item": "Food Item 1"},
{"rate": 100}
],
[
{"item": "Food Item 2"},
{"rate": 90}
],
[
{"item": "Food Item 3"},
{"rate": 80}
]
]}
]);
},
() => {
return frappe.tests.make("Restaurant Menu", [
{__newname: 'Restaurant Menu 2'},
{restaurant: "Test Restaurant 2"},
{items: [
[
{"item": "Food Item 1"},
{"rate": 105}
],
[
{"item": "Food Item 3"},
{"rate": 85}
]
]}
]);
},
() => frappe.set_route('Form', 'Restaurant', 'Test Restaurant 1'),
() => cur_frm.set_value('active_menu', 'Restaurant Menu 1'),
() => cur_frm.save(),
() => done()
]);
});

View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
test_records = [
dict(doctype='Item', item_code='Food Item 1',
item_group='Products', is_stock_item=0),
dict(doctype='Item', item_code='Food Item 2',
item_group='Products', is_stock_item=0),
dict(doctype='Item', item_code='Food Item 3',
item_group='Products', is_stock_item=0),
dict(doctype='Item', item_code='Food Item 4',
item_group='Products', is_stock_item=0),
dict(doctype='Restaurant Menu', restaurant='Test Restaurant 1', name='Test Restaurant 1 Menu 1',
items = [
dict(item='Food Item 1', rate=400),
dict(item='Food Item 2', rate=300),
dict(item='Food Item 3', rate=200),
dict(item='Food Item 4', rate=100),
]),
dict(doctype='Restaurant Menu', restaurant='Test Restaurant 1', name='Test Restaurant 1 Menu 2',
items = [
dict(item='Food Item 1', rate=450),
dict(item='Food Item 2', rate=350),
])
]
class TestRestaurantMenu(unittest.TestCase):
def test_price_list_creation_and_editing(self):
menu1 = frappe.get_doc('Restaurant Menu', 'Test Restaurant 1 Menu 1')
menu1.save()
menu2 = frappe.get_doc('Restaurant Menu', 'Test Restaurant 1 Menu 2')
menu2.save()
self.assertTrue(frappe.db.get_value('Price List', 'Test Restaurant 1 Menu 1'))
self.assertEquals(frappe.db.get_value('Item Price',
dict(price_list = 'Test Restaurant 1 Menu 1', item_code='Food Item 1'), 'price_list_rate'), 400)
self.assertEquals(frappe.db.get_value('Item Price',
dict(price_list = 'Test Restaurant 1 Menu 2', item_code='Food Item 1'), 'price_list_rate'), 450)
menu1.items[0].rate = 401
menu1.save()
self.assertEquals(frappe.db.get_value('Item Price',
dict(price_list = 'Test Restaurant 1 Menu 1', item_code='Food Item 1'), 'price_list_rate'), 401)
menu1.items[0].rate = 400
menu1.save()

View File

@ -0,0 +1,105 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2017-09-15 12:49:36.072636",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Currency",
"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": "Rate",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-09-15 14:18:55.145088",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Menu Item",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

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

View File

@ -0,0 +1,162 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Restaurant Order Entry', {
setup: function(frm) {
let get_item_query = () => {
return {
query: 'erpnext.restaurant.doctype.restaurant_order_entry.restaurant_order_entry.item_query_restaurant',
filters: {
'table': frm.doc.restaurant_table
}
};
};
frm.set_query('item', 'items', get_item_query);
frm.set_query('add_item', get_item_query);
},
onload_post_render: function(frm) {
if(!this.item_selector) {
this.item_selector = new erpnext.ItemSelector({
frm: frm,
item_field: 'item',
item_query: 'erpnext.restaurant.doctype.restaurant_order_entry.restaurant_order_entry.item_query_restaurant',
get_filters: () => {
return {table: frm.doc.restaurant_table};
}
});
}
let $input = frm.get_field('add_item').$input;
$input.on('keyup', function(e) {
if (e.which===13) {
if (frm.clear_item_timeout) {
clearTimeout (frm.clear_item_timeout);
}
// clear the item input so user can enter a new item
frm.clear_item_timeout = setTimeout (() => {
frm.set_value('add_item', '');
}, 1000);
let item = $input.val();
if (!item) return;
var added = false;
(frm.doc.items || []).forEach((d) => {
if (d.item===item) {
d.qty += 1;
added = true;
}
});
return frappe.run_serially([
() => {
if (!added) {
return frm.add_child('items', {item: item, qty: 1});
}
},
() => frm.get_field("items").refresh()
]);
}
});
},
refresh: function(frm) {
frm.disable_save();
frm.add_custom_button(__('Update'), () => {
return frm.trigger('sync');
});
frm.add_custom_button(__('Clear'), () => {
return frm.trigger('clear');
});
frm.add_custom_button(__('Bill'), () => {
return frm.trigger('make_invoice');
});
},
clear: function(frm) {
frm.doc.add_item = '';
frm.doc.grand_total = 0;
frm.doc.items = [];
frm.refresh();
frm.get_field('add_item').$input.focus();
},
restaurant_table: function(frm) {
// select the open sales order items for this table
if (!frm.doc.restaurant_table) {
return;
}
return frappe.call({
method: 'erpnext.restaurant.doctype.restaurant_order_entry.restaurant_order_entry.get_invoice',
args: {
table: frm.doc.restaurant_table
},
callback: (r) => {
frm.events.set_invoice_items(frm, r);
}
});
},
sync: function(frm) {
return frappe.call({
method: 'erpnext.restaurant.doctype.restaurant_order_entry.restaurant_order_entry.sync',
args: {
table: frm.doc.restaurant_table,
items: frm.doc.items
},
callback: (r) => {
frm.events.set_invoice_items(frm, r);
frappe.show_alert({message: __('Saved'), indicator: 'green'});
}
});
},
make_invoice: function(frm) {
frm.trigger('sync').then(() => {
frappe.prompt([
{
fieldname: 'customer',
label: __('Customer'),
fieldtype: 'Link',
reqd: 1,
options: 'Customer',
'default': frm.invoice.customer
},
{
fieldname: 'mode_of_payment',
label: __('Mode of Payment'),
fieldtype: 'Link',
reqd: 1,
options: 'Mode of Payment',
'default': frm.mode_of_payment || ''
}
], (data) => {
// cache this for next entry
frm.mode_of_payment = data.mode_of_payment;
return frappe.call({
method: 'erpnext.restaurant.doctype.restaurant_order_entry.restaurant_order_entry.make_invoice',
args: {
table: frm.doc.restaurant_table,
customer: data.customer,
mode_of_payment: data.mode_of_payment
},
callback: (r) => {
frm.set_value('last_sales_invoice', r.message);
frm.trigger('clear');
}
});
},
__("Select Customer"));
});
},
set_invoice_items: function(frm, r) {
let invoice = r.message;
frm.doc.items = [];
(invoice.items || []).forEach((d) => {
frm.add_child('items', {item: d.item_code, qty: d.qty, rate: d.rate});
});
frm.set_value('grand_total', invoice.grand_total);
frm.set_value('last_sales_invoice', invoice.name);
frm.invoice = invoice;
frm.refresh();
}
});

View File

@ -0,0 +1,280 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2017-09-15 15:10:24.530365",
"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": "restaurant_table",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Restaurant Table",
"length": 0,
"no_copy": 0,
"options": "Restaurant Table",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "restaurant_table",
"description": "Click Enter To Add",
"fieldname": "add_item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Add Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grand_total",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Grand Total",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "last_sales_invoice",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Last Sales Invoice",
"length": 0,
"no_copy": 0,
"options": "Sales Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "restaurant_table",
"fieldname": "current_order",
"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": "Current Order",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "restaurant_table",
"fieldname": "items",
"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": "Items",
"length": 0,
"no_copy": 0,
"options": "Restaurant Order Entry Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-04 17:06:20.926999",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Order Entry",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "Restaurant Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, json
from frappe.model.document import Document
from frappe import _
from erpnext.controllers.queries import item_query
class RestaurantOrderEntry(Document):
pass
@frappe.whitelist()
def get_invoice(table):
'''returns the active invoice linked to the given table'''
invoice_name = frappe.get_value('Sales Invoice', dict(restaurant_table = table, docstatus=0))
restaurant, menu_name = get_restaurant_and_menu_name(table)
if invoice_name:
invoice = frappe.get_doc('Sales Invoice', invoice_name)
else:
invoice = frappe.new_doc('Sales Invoice')
invoice.naming_series = frappe.db.get_value('Restaurant', restaurant, 'invoice_series_prefix')
invoice.is_pos = 1
default_customer = frappe.db.get_value('Restaurant', restaurant, 'default_customer')
if not default_customer:
frappe.throw(_('Please set default customer in Restaurant Settings'))
invoice.customer = default_customer
invoice.taxes_and_charges = frappe.db.get_value('Restaurant', restaurant, 'default_tax_template')
invoice.selling_price_list = frappe.db.get_value('Price List', dict(restaurant_menu=menu_name, enabled=1))
return invoice
@frappe.whitelist()
def sync(table, items):
'''Sync the sales order related to the table'''
invoice = get_invoice(table)
items = json.loads(items)
invoice.items = []
invoice.restaurant_table = table
for d in items:
invoice.append('items', dict(
item_code = d.get('item'),
qty = d.get('qty')
))
invoice.save()
return invoice.as_dict()
@frappe.whitelist()
def make_invoice(table, customer, mode_of_payment):
'''Make table based on Sales Order'''
restaurant, menu = get_restaurant_and_menu_name(table)
invoice = get_invoice(table)
invoice.customer = customer
invoice.restaurant = restaurant
invoice.calculate_taxes_and_totals()
invoice.append('payments', dict(mode_of_payment=mode_of_payment, amount=invoice.grand_total))
invoice.save()
invoice.submit()
frappe.msgprint(_('Invoice Created'), indicator='green', alert=True)
return invoice.name
def item_query_restaurant(doctype='Item', txt='', searchfield='name', start=0, page_len=20, filters=None, as_dict=False):
'''Return items that are selected in active menu of the restaurant'''
restaurant, menu = get_restaurant_and_menu_name(filters['table'])
items = frappe.db.get_all('Restaurant Menu Item', ['item'], dict(parent = menu))
del filters['table']
filters['name'] = ('in', [d.item for d in items])
return item_query('Item', txt, searchfield, start, page_len, filters, as_dict)
def get_restaurant_and_menu_name(table):
if not table:
frappe.throw(_('Please select a table'))
restaurant = frappe.db.get_value('Restaurant Table', table, 'restaurant')
menu = frappe.db.get_value('Restaurant', restaurant, 'active_menu')
if not menu:
frappe.throw(_('Please set an active menu for Restaurant {0}').format(restaurant))
return restaurant, menu

View File

@ -0,0 +1,53 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Restaurant Order Entry", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(5);
frappe.run_serially([
// insert a new Restaurant Order Entry
() => frappe.set_route('Form', 'Restaurant Settings'),
() => cur_frm.set_value('default_customer', 'Test Customer 1'),
() => cur_frm.save(),
() => frappe.set_route('Form', 'Restaurant Order Entry'),
() => frappe.click_button('Clear'),
() => frappe.timeout(2),
() => cur_frm.set_value('restaurant_table', 'Test-Restaurant-1-01'),
() => cur_frm.set_value('add_item', 'Food Item 1'),
() => frappe.timeout(0.5),
() => {
var e = $.Event( "keyup", {which: 13} );
$('input[data-fieldname="add_item"]').trigger(e);
return frappe.timeout(0.5);
},
() => cur_frm.set_value('add_item', 'Food Item 1'),
() => {
var e = $.Event( "keyup", {which: 13} );
$('input[data-fieldname="add_item"]').trigger(e);
return frappe.timeout(0.5);
},
() => cur_frm.set_value('add_item', 'Food Item 2'),
() => {
var e = $.Event( "keyup", {which: 13} );
$('input[data-fieldname="add_item"]').trigger(e);
return frappe.timeout(0.5);
},
() => {
assert.equal(cur_frm.doc.items[0].item, 'Food Item 1');
assert.equal(cur_frm.doc.items[0].qty, 2);
assert.equal(cur_frm.doc.items[1].item, 'Food Item 2');
assert.equal(cur_frm.doc.items[1].qty, 1);
},
() => frappe.click_button('Update'),
() => frappe.timeout(2),
() => {
assert.equal(cur_frm.doc.grand_total, 290);
}
() => done()
]);
});

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe, json
import unittest
from erpnext.restaurant.doctype.restaurant_order_entry.restaurant_order_entry \
import (sync, make_invoice, item_query_restaurant)
class TestRestaurantOrderEntry(unittest.TestCase):
def setUp(self):
# save the menus as Price List is deleted before tests...
frappe.get_doc('Restaurant Menu', 'Test Restaurant 1 Menu 1').save()
frappe.get_doc('Restaurant Menu', 'Test Restaurant 1 Menu 2').save()
if not frappe.db.get_value('Restaurant', 'Test Restaurant 1', 'active_menu'):
restaurant = frappe.get_doc('Restaurant', 'Test Restaurant 1')
restaurant.active_menu = 'Test Restaurant 1 Menu 1'
restaurant.save()
def test_update_order(self):
table = frappe.db.get_value('Restaurant Table', dict(restaurant = 'Test Restaurant 1'))
invoice = sync(table,
json.dumps([dict(item='Food Item 1', qty = 10), dict(item='Food Item 2', qty = 2)]))
self.assertEquals(invoice.get('restaurant_table'), table)
self.assertEquals(invoice.get('items')[0].get('item_code'), 'Food Item 1')
self.assertEquals(invoice.get('items')[1].get('item_code'), 'Food Item 2')
self.assertEquals(invoice.get('net_total'), 4600)
return table
def test_billing(self):
table = self.test_update_order()
invoice_name = make_invoice(table, '_Test Customer', 'Cash')
sales_invoice = frappe.get_doc('Sales Invoice', invoice_name)
self.assertEquals(sales_invoice.grand_total, 4600)
self.assertEquals(sales_invoice.items[0].item_code, 'Food Item 1')
self.assertEquals(sales_invoice.items[1].item_code, 'Food Item 2')
self.assertEquals(sales_invoice.payments[0].mode_of_payment, 'Cash')
self.assertEquals(sales_invoice.payments[0].amount, 4600)
def test_item_query(self):
table = frappe.db.get_value('Restaurant Table', dict(restaurant = 'Test Restaurant 1'))
result = item_query_restaurant(filters=dict(table=table))
items = [d[0] for d in result]
self.assertTrue('Food Item 1' in items)
self.assertTrue('_Test Item 1' not in items)

View File

@ -0,0 +1,163 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-09-15 15:11:50.313241",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "qty",
"fieldtype": "Int",
"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": "Qty",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "served",
"fieldtype": "Int",
"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": "Served",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Currency",
"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": "Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-09-21 08:39:27.232175",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Order Entry Item",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

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

View File

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

View File

@ -0,0 +1,337 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "REST.######",
"beta": 0,
"creation": "2017-09-15 13:05:51.063661",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nWaitlisted\nCancelled\nNo Show\nSuccess",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "restaurant",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Restaurant",
"length": 0,
"no_copy": 0,
"options": "Restaurant",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "no_of_people",
"fieldtype": "Int",
"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": "No of People",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reservation_time",
"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": "Reservation Time",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reservation_end_time",
"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": "Reservation End Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "customer_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Customer Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "contact_number",
"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": "Contact Number",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-15 14:40:56.759315",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Reservation",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Restaurant Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from datetime import timedelta
from frappe.utils import get_datetime
class RestaurantReservation(Document):
def validate(self):
if not self.reservation_end_time:
self.reservation_end_time = get_datetime(self.reservation_time) + timedelta(hours=1)
@frappe.whitelist()
def get_events(start, end, filters=None):
"""Returns events for Gantt / Calendar view rendering.
:param start: Start date-time.
:param end: End date-time.
:param filters: Filters (JSON).
"""
from frappe.desk.calendar import get_event_conditions
conditions = get_event_conditions("Restaurant Reservation", filters)
data = frappe.db.sql("""select name, reservation_time,
reservation_end_time, customer_name, status, no_of_people
from
`tabRestaurant Reservation`
where
((ifnull(reservation_time, '0000-00-00')!= '0000-00-00') \
and (reservation_time <= %(end)s) \
or ((ifnull(reservation_end_time, '0000-00-00')!= '0000-00-00') \
and reservation_end_time >= %(start)s))
{conditions}""".format(conditions=conditions), {
"start": start,
"end": end
}, as_dict=True, update={"allDay": 0})
return data

View File

@ -0,0 +1,18 @@
frappe.views.calendar["Restaurant Reservation"] = {
field_map: {
"start": "reservation_time",
"end": "reservation_end_time",
"id": "name",
"title": "customer_name",
"allDay": "allDay",
},
gantt: true,
filters: [
{
"fieldtype": "Data",
"fieldname": "customer_name",
"label": __("Customer Name")
}
],
get_events_method: "erpnext.restaurant.doctype.restaurant_reservation.restaurant_reservation.get_events"
};

View File

@ -0,0 +1,27 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Restaurant Reservation", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Restaurant Reservation
() => frappe.tests.make('Restaurant Reservation', [
// values to be set
{restaurant: 'Gokul - JP Nagar'},
{customer_name: 'test customer'},
{reservation_time: frappe.datetime.now_date() + " 19:00:00"},
{no_of_people: 4},
]),
() => {
assert.equal(cur_frm.doc.reservation_end_time,
frappe.datetime.now_date() + ' 20:00:00');
},
() => done()
]);
});

View File

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

View File

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

View File

@ -0,0 +1,156 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2017-09-15 12:45:24.717355",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "restaurant",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Restaurant",
"length": 0,
"no_copy": 0,
"options": "Restaurant",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "no_of_seats",
"fieldtype": "Int",
"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": "No of Seats",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "minimum_seating",
"fieldtype": "Int",
"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": "Minimum Seating",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-15 13:18:05.254106",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Table",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Restaurant Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Hospitality",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, re
from frappe.model.document import Document
from frappe.model.naming import make_autoname
class RestaurantTable(Document):
def autoname(self):
prefix = re.sub('-+', '-', self.restaurant.replace(' ', '-'))
self.name = make_autoname(prefix + '-.##')

View File

@ -0,0 +1,41 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Restaurant Table", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(0);
frappe.run_serially([
// insert a new Restaurant Table
() => frappe.tests.make('Restaurant Table', [
// values to be set
{restaurant: 'Test Restaurant 1'},
{no_of_seats: 4},
]),
() => frappe.tests.make('Restaurant Table', [
// values to be set
{restaurant: 'Test Restaurant 1'},
{no_of_seats: 5},
]),
() => frappe.tests.make('Restaurant Table', [
// values to be set
{restaurant: 'Test Restaurant 1'},
{no_of_seats: 2},
]),
() => frappe.tests.make('Restaurant Table', [
// values to be set
{restaurant: 'Test Restaurant 1'},
{no_of_seats: 2},
]),
() => frappe.tests.make('Restaurant Table', [
// values to be set
{restaurant: 'Test Restaurant 1'},
{no_of_seats: 6},
]),
() => done()
]);
});

View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
test_records = [
dict(restaurant='Test Restaurant 1', no_of_seats=5, minimum_seating=1),
dict(restaurant='Test Restaurant 1', no_of_seats=5, minimum_seating=1),
dict(restaurant='Test Restaurant 1', no_of_seats=5, minimum_seating=1),
dict(restaurant='Test Restaurant 1', no_of_seats=5, minimum_seating=1),
]
class TestRestaurantTable(unittest.TestCase):
pass

View File

@ -936,7 +936,7 @@ class POSItems {
const all_items = Object.values(_items).map(item => this.get_item_html(item));
let row_items = [];
const row_container = '<div style="display: flex; border-bottom: 1px solid #ebeff2">';
const row_container = '<div class="image-view-row">';
let curr_row = row_container;
for (let i=0; i < all_items.length; i++) {

View File

@ -1,178 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def get_domain(domain):
'''Written as a function to prevent data mutation effects'''
data = {
'Manufacturing': {
'desktop_icons': ['Item', 'BOM', 'Customer', 'Supplier', 'Sales Order',
'Production Order', 'Stock Entry', 'Purchase Order', 'Task', 'Buying', 'Selling',
'Accounts', 'HR', 'ToDo'],
'remove_roles': ['Academics User', 'Instructor', 'Physician', 'Nursing User',
'Laboratory user', 'LabTest Approver', 'Healthcare Administrator'],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'collapsible_depends_on', 'value': 'is_stock_item'},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
},
'Retail': {
'desktop_icons': ['POS', 'Item', 'Customer', 'Sales Invoice', 'Purchase Order',
'Warranty Claim', 'Accounts', 'Task', 'Buying', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor',
'Physician', 'Nursing User', 'Laboratory user',
'LabTest Approver', 'Healthcare Administrator'],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1},
{'doctype': 'Customer', 'fieldname': 'credit_limit_section', 'property': 'hidden', 'value': 1},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
},
'Distribution': {
'desktop_icons': ['Item', 'Customer', 'Supplier', 'Lead', 'Sales Order', 'Task',
'Sales Invoice', 'CRM', 'Selling', 'Buying', 'Stock', 'Accounts', 'HR', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor',
'Physician', 'Nursing User', 'Laboratory user',
'LabTest Approver', 'Healthcare Administrator'],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
},
'Services': {
'desktop_icons': ['Project', 'Timesheet', 'Customer', 'Sales Order', 'Sales Invoice',
'Lead', 'Opportunity', 'Task', 'Expense Claim', 'Employee', 'HR', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor',
'Physician', 'Nursing User', 'Laboratory user',
'LabTest Approver', 'Healthcare Administrator'],
'properties': [
{'doctype': 'Item', 'fieldname': 'is_stock_item', 'property': 'default', 'value': 0},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 0]
],
'default_portal_role': 'Customer'
},
'Education': {
'desktop_icons': ['Student', 'Program', 'Course', 'Student Group', 'Instructor',
'Fees', 'Task', 'ToDo', 'Schools'],
'allow_roles': ['Academics User', 'Accounts User', 'Accounts Manager', 'Item Manager',
'Website Manager', 'HR User', 'HR Manager', 'Purchase User', 'Purchase Manager',
'Student', 'Projects User', 'Instructor'],
'default_portal_role': 'Student'
},
'Healthcare': {
'desktop_icons': ['Patient', 'Patient Appointment', 'Consultation', 'Lab Test', 'Healthcare',
'Accounts', 'Buying', 'Stock', 'HR', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Projects User', 'Projects Manager',
'Academics User', 'Instructor'],
'default_portal_role': 'Patient'
},
}
if not domain in data:
raise 'Invalid Domain {0}'.format(domain)
return frappe._dict(data[domain])
def setup_domain(domain):
'''Setup roles, desktop icons, properties, values, portal sidebar menu based on domain'''
data = get_domain(domain)
setup_roles(data)
setup_desktop_icons(data)
setup_properties(data)
set_values(data)
setup_sidebar_items(data)
update_module_def_restrict_to_domain()
if data.get('default_portal_role'):
frappe.db.set_value('Portal Settings', None, 'default_role', data.get('default_portal_role'))
frappe.clear_cache()
def setup_desktop_icons(data):
'''set desktop icons form `data.desktop_icons`'''
from frappe.desk.doctype.desktop_icon.desktop_icon import set_desktop_icons
if data.desktop_icons:
set_desktop_icons(data.desktop_icons)
def setup_properties(data):
if data.properties:
for args in data.properties:
frappe.make_property_setter(args)
def setup_roles(data):
'''Add, remove roles from `data.allow_roles` or `data.remove_roles`'''
def remove_role(role):
frappe.db.sql('delete from `tabHas Role` where role=%s', role)
frappe.set_value('Role', role, 'disabled', 1)
if data.remove_roles:
for role in data.remove_roles:
remove_role(role)
if data.allow_roles:
# remove all roles other than allowed roles
active_domains = frappe.get_active_domains()
data.allow_roles += ['Administrator', 'Guest', 'System Manager', 'All']
for role in frappe.get_all('Role', filters = {"restrict_to_domain": ("not in", active_domains)}):
if not (role.name in data.allow_roles):
remove_role(role.name)
def set_values(data):
'''set values based on `data.set_value`'''
if data.set_value:
for args in data.set_value:
doc = frappe.get_doc(args[0], args[1] or args[0])
doc.set(args[2], args[3])
doc.save()
def setup_sidebar_items(data):
'''Enable / disable sidebar items'''
if data.allow_sidebar_items:
# disable all
frappe.db.sql('update `tabPortal Menu Item` set enabled=0')
# enable
frappe.db.sql('''update `tabPortal Menu Item` set enabled=1
where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in data.allow_sidebar_items])))
if data.remove_sidebar_items:
# disable all
frappe.db.sql('update `tabPortal Menu Item` set enabled=1')
# enable
frappe.db.sql('''update `tabPortal Menu Item` set enabled=0
where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in data.remove_sidebar_items])))
def reset():
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
add_all_roles_to('Administrator')
frappe.db.sql('delete from `tabProperty Setter`')
def update_module_def_restrict_to_domain():
""" set the restrict to domain for the module def """
module_def_restrict_to_domain_mapper = {
"Schools": 'Education'
}
lang = frappe.db.get_single_value("System Settings", "language") or "en"
for module, domain in module_def_restrict_to_domain_mapper.iteritems():
if frappe.db.exists("Domain", _(domain, lang)):
frappe.db.set_value("Module Def", module, "restrict_to_domain", _(domain, lang))
elif frappe.db.exists("Domain", domain):
frappe.db.set_value("Module Def", module, "restrict_to_domain", domain)
else:
pass

View File

@ -10,12 +10,10 @@ from frappe.utils import cstr, flt, getdate
from frappe import _
from frappe.utils.file_manager import save_file
from .default_website import website_maker
from .healthcare import setup_healthcare
import install_fixtures
from .sample_data import make_sample_data
from erpnext.accounts.doctype.account.account import RootNotEditable
from frappe.core.doctype.communication.comment import add_info_comment
from erpnext.setup.setup_wizard.domainify import setup_domain
from erpnext.setup.doctype.company.company import install_country_fixtures
def setup_complete(args=None):
@ -35,20 +33,14 @@ def setup_complete(args=None):
create_letter_head(args)
set_no_copy_fields_in_variant_settings()
if args.get('domain').lower() == 'education':
create_academic_year()
create_academic_term()
if args.domain.lower() == 'healthcare':
setup_healthcare()
if args.get('setup_website'):
website_maker(args)
create_logo(args)
frappe.local.message_log = []
setup_domain(args.get('domain'))
domain_settings = frappe.get_single('Domain Settings')
domain_settings.set_active_domains([args.get('domain')])
frappe.db.commit()
login_as_first_user(args)
@ -400,27 +392,3 @@ def create_employee_for_self(args):
emp.flags.ignore_mandatory = True
emp.insert(ignore_permissions = True)
# Schools
def create_academic_term():
at = ["Semester 1", "Semester 2", "Semester 3"]
ay = ["2013-14", "2014-15", "2015-16", "2016-17", "2017-18"]
for y in ay:
for t in at:
academic_term = frappe.new_doc("Academic Term")
academic_term.academic_year = y
academic_term.term_name = t
try:
academic_term.save()
except frappe.DuplicateEntryError:
pass
def create_academic_year():
ac = ["2013-14", "2014-15", "2015-16", "2016-17", "2017-18"]
for d in ac:
academic_year = frappe.new_doc("Academic Year")
academic_year.academic_year_name = d
try:
academic_year.save()
except frappe.DuplicateEntryError:
pass

View File

@ -16,44 +16,50 @@ def run_setup_wizard_test():
# Language slide
driver.wait_for_ajax(True)
time.sleep(2)
time.sleep(1)
driver.set_select("language", "English (United States)")
driver.wait_for_ajax(True)
driver.wait_till_clickable(".next-btn").click()
time.sleep(1)
driver.click(".next-btn")
# Region slide
driver.wait_for_ajax(True)
driver.set_select("country", "India")
driver.wait_for_ajax(True)
driver.wait_till_clickable(".next-btn").click()
time.sleep(1)
driver.click(".next-btn")
# Profile slide
driver.set_field("full_name", "Great Tester")
driver.set_field("email", "great@example.com")
driver.set_field("password", "test")
driver.wait_till_clickable(".next-btn").click()
driver.wait_for_ajax(True)
time.sleep(1)
driver.click(".next-btn")
time.sleep(1)
# Brand slide
# domain slide
driver.set_select("domain", "Manufacturing")
time.sleep(5)
driver.wait_till_clickable(".next-btn").click()
time.sleep(1)
driver.click(".next-btn")
# Org slide
driver.set_field("company_name", "For Testing")
driver.wait_till_clickable(".next-btn").click()
time.sleep(1)
driver.print_console()
driver.click(".next-btn")
driver.set_field("company_tagline", "Just for GST")
driver.set_field("bank_account", "HDFC")
driver.wait_till_clickable(".complete-btn").click()
time.sleep(3)
driver.click(".complete-btn")
# Wait for desktop
driver.wait_for('#page-desktop', timeout=600)
console = driver.get_console()
if frappe.flags.tests_verbose:
for line in console:
print(line)
print('-' * 40)
time.sleep(1)
driver.print_console()
time.sleep(3)
frappe.db.set_default('in_selenium', None)
frappe.db.set_value("Company", "For Testing", "write_off_account", "Write Off - FT")

View File

@ -111,27 +111,12 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None):
def enable_all_roles_and_domains():
""" enable all roles and domain for testing """
roles = frappe.get_list("Role", filters={"disabled": 1})
for role in roles:
_role = frappe.get_doc("Role", role.get("name"))
_role.disabled = 0
_role.flags.ignore_mandatory = True
_role.flags.ignore_permissions = True
_role.save()
# add all roles to users
if roles:
user = frappe.get_doc("User", "Administrator")
user.add_roles(*[role.get("name") for role in roles])
domains = frappe.get_list("Domain")
domains = frappe.get_all("Domain")
if not domains:
return
domain_settings = frappe.get_doc("Domain Settings", "Domain Settings")
domain_settings.set("active_domains", [])
for domain in domains:
row = domain_settings.append("active_domains", {})
row.domain=domain.get("name")
domain_settings.save()
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
frappe.get_single('Domain Settings').set_active_domains(\
[d.name for d in domains])
add_all_roles_to('Administrator')

View File

@ -231,7 +231,9 @@ QUnit.test('Make fixtures', assert => {
let done = assert.async();
let tasks = [];
Object.keys(frappe.test_data).forEach(function(doctype) {
tasks.push(function() { return frappe.tests.setup_doctype(doctype); });
tasks.push(function() {
return frappe.tests.setup_doctype(doctype, frappe.test_data[doctype]);
});
});
frappe.run_serially(tasks).then(() => done());
});

View File

@ -129,4 +129,8 @@ erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js
erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js
erpnext/restaurant/doctype/restaurant/test_restaurant.js
erpnext/restaurant/doctype/test_restaurant_table/test_restaurant_table.js
erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js
erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.js