[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
@ -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")
|
||||
}
|
||||
]
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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('%', '%%')),
|
||||
{
|
||||
|
BIN
erpnext/docs/assets/img/restaurant/order-entry-bill.png
Normal file
After Width: | Height: | Size: 207 KiB |
BIN
erpnext/docs/assets/img/restaurant/order-entry.png
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
erpnext/docs/assets/img/restaurant/reservation-kanban.png
Normal file
After Width: | Height: | Size: 208 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-invoice.png
Normal file
After Width: | Height: | Size: 195 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-menu.png
Normal file
After Width: | Height: | Size: 202 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-reservation.png
Normal file
After Width: | Height: | Size: 223 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-table.png
Normal file
After Width: | Height: | Size: 201 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant.png
Normal file
After Width: | Height: | Size: 260 KiB |
9
erpnext/docs/user/manual/en/hospitality/index.md
Normal 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}
|
4
erpnext/docs/user/manual/en/hospitality/index.txt
Normal file
@ -0,0 +1,4 @@
|
||||
restaurant
|
||||
restaurant-menu
|
||||
reservations
|
||||
order-entry
|
26
erpnext/docs/user/manual/en/hospitality/order-entry.md
Normal 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">
|
||||
|
13
erpnext/docs/user/manual/en/hospitality/reservations.md
Normal 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">
|
@ -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">
|
19
erpnext/docs/user/manual/en/hospitality/restaurant.md
Normal 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">
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
18
erpnext/domains/distribution.py
Normal 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'
|
||||
}
|
37
erpnext/domains/education.py
Normal 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')
|
||||
]
|
||||
}
|
29
erpnext/domains/healthcare.py
Normal 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'
|
||||
}
|
32
erpnext/domains/hospitality.py
Normal 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'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
25
erpnext/domains/manufacturing.py
Normal 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
@ -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'
|
||||
}
|
22
erpnext/domains/services.py
Normal 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'
|
||||
}
|
@ -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 = [
|
@ -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"]
|
||||
|
||||
|
@ -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') &&
|
||||
|
@ -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()
|
||||
|
@ -16,3 +16,4 @@ Maintenance
|
||||
Schools
|
||||
Regional
|
||||
Healthcare
|
||||
Restaurant
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
@ -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()
|
@ -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>
|
@ -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}));
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
0
erpnext/restaurant/doctype/__init__.py
Normal file
0
erpnext/restaurant/doctype/restaurant/__init__.py
Normal file
10
erpnext/restaurant/doctype/restaurant/restaurant.js
Normal 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');
|
||||
});
|
||||
}
|
||||
});
|
309
erpnext/restaurant/doctype/restaurant/restaurant.json
Normal 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
|
||||
}
|
10
erpnext/restaurant/doctype/restaurant/restaurant.py
Normal 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
|
@ -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']
|
||||
}
|
||||
]
|
||||
}
|
37
erpnext/restaurant/doctype/restaurant/test_restaurant.js
Normal 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()
|
||||
]);
|
||||
});
|
17
erpnext/restaurant/doctype/restaurant/test_restaurant.py
Normal 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
|
@ -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');
|
||||
},
|
||||
});
|
247
erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.json
Normal 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
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
]);
|
||||
|
||||
});
|
@ -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()
|
@ -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
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
||||
});
|
@ -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
|
||||
}
|
@ -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
|
@ -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()
|
||||
]);
|
||||
|
||||
});
|
@ -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)
|
@ -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
|
||||
}
|
@ -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
|
@ -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) {
|
||||
|
||||
}
|
||||
});
|
@ -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
|
||||
}
|
@ -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
|
@ -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"
|
||||
};
|
@ -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()
|
||||
]);
|
||||
|
||||
});
|
@ -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
|
@ -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) {
|
||||
|
||||
}
|
||||
});
|
@ -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
|
||||
}
|
@ -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 + '-.##')
|
@ -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()
|
||||
]);
|
||||
|
||||
});
|
@ -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
|
@ -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++) {
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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')
|
||||
|
@ -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());
|
||||
});
|
||||
|
@ -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
|
||||
|