Merge branch 'master' of uflows.com:/var/git/qwaiter

This commit is contained in:
2015-05-06 11:57:14 +02:00
56 changed files with 372 additions and 12245 deletions
+2
View File
@@ -44,3 +44,5 @@ erl_crash.dump
/.project /.project
# dia # dia
/*.autosave /*.autosave
/db/data
/db/config
+5 -5
View File
@@ -1,6 +1,6 @@
GIT GIT
remote: git://github.com/bterkuile/cmtool.git remote: git://github.com/bterkuile/cmtool.git
revision: e937070277ae7d44e37de85864f91a1fc779f093 revision: 1b82ab178409684e504e70b6403bc4eb8425c432
specs: specs:
cmtool (1.0.0) cmtool (1.0.0)
bourbon bourbon
@@ -101,7 +101,7 @@ GEM
bcrypt (3.1.10) bcrypt (3.1.10)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bourbon (4.2.1) bourbon (4.2.2)
sass (~> 3.4) sass (~> 3.4)
thor thor
builder (3.2.2) builder (3.2.2)
@@ -239,7 +239,7 @@ GEM
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
gherkin (2.12.2) gherkin (2.12.2)
multi_json (~> 1.3) multi_json (~> 1.3)
globalid (0.3.3) globalid (0.3.4)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
handlebars-source (1.3.0) handlebars-source (1.3.0)
hashie (3.4.0) hashie (3.4.0)
@@ -341,7 +341,7 @@ GEM
rails-assets-qunit (1.17.1) rails-assets-qunit (1.17.1)
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.5) rails-dom-testing (1.0.6)
activesupport (>= 4.2.0.beta, < 5.0) activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0) nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
@@ -440,7 +440,7 @@ GEM
eventmachine (~> 1.0) eventmachine (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.4) thread_safe (0.3.5)
tilt (1.4.1) tilt (1.4.1)
timers (4.0.1) timers (4.0.1)
hitimes hitimes
@@ -24,8 +24,11 @@ App.MenuProductComponent = Ember.Component.extend
makeEditable: -> @set('editMode', true) makeEditable: -> @set('editMode', true)
save: -> save: ->
return unless @get('product.isValid') return unless @get('product.isValid')
@get('product.product_variants').forEach (product_variant)->
product_variant.save() if product_variant.get('isDirty')
if @get('product.isDirty') if @get('product.isDirty')
@get('product').save().then((=> @set 'editMode', false), (-> true)) @get('product').save()
@set 'editMode', false
destroyProduct: (product)-> destroyProduct: (product)->
if product.get('isNew') if product.get('isNew')
product.deleteRecord() product.deleteRecord()
@@ -38,8 +41,15 @@ App.MenuProductComponent = Ember.Component.extend
if @get('product.isNew') if @get('product.isNew')
@get('product').deleteRecord() @get('product').deleteRecord()
else else
@get('product.product_variants').forEach (product_variant)->
product_variant.rollback()
@get('product').rollback() @get('product').rollback()
@set 'editMode', false @set 'editMode', false
addProductVariant: ->
product_variant = @get('targetObject.store').createRecord('product_variant')
@get('product.product_variants').addObject product_variant
removeProductVariant: (product_variant)->
product_variant.destroyRecord()
didInsertElement: -> didInsertElement: ->
@set 'editMode', true if @get('product.isNew') @set 'editMode', true if @get('product.isNew')
namePlaceholder: (-> t "attributes.product.name").property() namePlaceholder: (-> t "attributes.product.name").property()
@@ -34,9 +34,8 @@ App.Order = DS.Model.extend
@get('product_orders').getEach('total').reduce(((sum, total) -> sum + total), 0) @get('product_orders').getEach('total').reduce(((sum, total) -> sum + total), 0)
).property('product_orders.@each.total') ).property('product_orders.@each.total')
display: (-> display: Ember.computed 'product_orders.@each.display', ->
@get('product_orders').map((po) -> "#{po.get('quantity')} x #{po.get('product.name')}").join(', ') @get('product_orders').map((po) -> po.get('display')).join(', ')
).property('product_orders.@each.quantity', 'product_orders.@each.product.@each.name')
display_with_table: (-> display_with_table: (->
table = t('models.table').toLowerCase() table = t('models.table').toLowerCase()
@@ -10,6 +10,7 @@ App.Product = DS.Model.extend Ember.Validations.Mixin,
image: attr() image: attr()
product_category: DS.belongsTo('product_category') product_category: DS.belongsTo('product_category')
product_orders: DS.hasMany('product_order') product_orders: DS.hasMany('product_order')
product_variants: DS.hasMany('product_variant')
image_src: (-> image_src: (->
image = @get('image') image = @get('image')
@@ -18,6 +19,12 @@ App.Product = DS.Model.extend Ember.Validations.Mixin,
image image
).property('image') ).property('image')
sorted_product_variants: Ember.computed 'product_variants.@each.name', 'product_variants.@each.position', ->
@get('product_variants').sortBy('position')
variantsDisplay: Ember.computed 'sorted_product_variants', ->
@get('sorted_product_variants').mapBy('name').join(', ')
#isValid: (-> #isValid: (->
#return false unless price = @get('price') #return false unless price = @get('price')
#return false unless "#{price}".match(/^[+-]?\d+(\.?\d?\d)?$/) #return false unless "#{price}".match(/^[+-]?\d+(\.?\d?\d)?$/)
@@ -2,8 +2,14 @@ attr = DS.attr
App.ProductOrder = DS.Model.extend App.ProductOrder = DS.Model.extend
quantity: attr 'number', defaultValue: 1 quantity: attr 'number', defaultValue: 1
price: attr 'number' price: attr 'number'
product_variant: attr('string')
product: DS.belongsTo('product', async: true) product: DS.belongsTo('product', async: true)
order: DS.belongsTo('order') order: DS.belongsTo('order')
increment: -> increment: ->
@set('quantity', @get('quantity') + 1) @set('quantity', @get('quantity') + 1)
total: (-> @get('quantity') * @get('price')).property('quantity', 'price') total: (-> @get('quantity') * @get('price')).property('quantity', 'price')
display: Ember.computed 'quantity', 'product_variant', 'product.name', ->
disp = "#{@get('quantity')} x #{@get('product.name')}"
if variant = @get('product_variant')
disp = "#{disp} (#{variant})"
disp.htmlSafe()
@@ -0,0 +1,8 @@
attr = DS.attr
App.ProductVariant = DS.Model.extend Ember.Validations.Mixin,
name: attr 'string'
product: DS.belongsTo 'product'
position: attr 'number', defaultValue: 0
validations:
name:
presence: true
@@ -3,14 +3,14 @@ if editMode
.small-6.medium-3.columns.name .small-6.medium-3.columns.name
= input value=product.name placeholder=namePlaceholder action="save" = input value=product.name placeholder=namePlaceholder action="save"
= errors product.errors.name = errors product.errors.name
.small-6.medium-3.columns.actions
a.rollback-product-action{action "rollbackProduct"}: span
a.destroy-product-action{action "destroyProduct" product}: span
a.save-product-action{action "save"}: span
.small-6.medium-3.columns.price .small-6.medium-3.columns.price
= edit-currency value=product.price action="save" = edit-currency value=product.price action="save"
= errors product.errors.price = errors product.errors.price
.small-6.medium-3.columns.code= input value=product.code placeholder=codePlaceholder .small-6.medium-3.columns.code= input value=product.code placeholder=codePlaceholder
.small-6.medium-3.columns.actions
a.rollback-product-action{action "rollbackProduct"}: span
a.destroy-product-action{action "destroyProduct" product}: span
a.save-product-action{action "save"}: span
.row .row
.small-3.columns= t 'attributes.product.active' .small-3.columns= t 'attributes.product.active'
.small-9.columns: view boolean-switch value=product.active .small-9.columns: view boolean-switch value=product.active
@@ -21,6 +21,17 @@ if editMode
.small-12.medium-6.columns .small-12.medium-6.columns
= view "upload-file" name="image" accept="image/*" file=product.image = view "upload-file" name="image" accept="image/*" file=product.image
img src=product.image_src img src=product.image_src
each product_variant in product.product_variants
.row
.small-1.columns &nbsp;
.small-8.medium-5.large-4.columns= input value=product_variant.name
.small-2.medium-6.large-7.columns
button.remove-product-variant{action "removeProductVariant" product_variant}: span
.row
.small-12.columns
button.add-product-variant{ action "addProductVariant"}
span.icon
= t 'product_variant.add_product_variant'
else else
if showProduct if showProduct
.row .row
@@ -34,3 +45,6 @@ else
.small-3.columns .small-3.columns
a.edit-product-action{action "makeEditable"}: span a.edit-product-action{action "makeEditable"}: span
/img src=product.image_src /img src=product.image_src
if product.product_variants
.row
.small-12.columns= product.variantsDisplay
@@ -13,4 +13,4 @@ form.form-horizontal
.form-field= number-field numericValue=number_end .form-field= number-field numericValue=number_end
hr hr
button.modal-close{action "close"}=t 'section.add_tables.modal.close_button' button.modal-close{action "close"}=t 'section.add_tables.modal.close_button'
button.modal-confirm.right{action "addTables"}=t 'section.add_tables.modal.add_button' button.modal-confirm.right{action "ok"}=t 'section.add_tables.modal.add_button'
@@ -21,7 +21,6 @@
@ttry = (path, vars={})-> @ttry = (path, vars={})->
@t(path, $.extend(vars, emptyWhenNotFound: true)) @t(path, $.extend(vars, emptyWhenNotFound: true))
# return translation in the form # return translation in the form
# <span data-t="models.table">Tafel</span> # <span data-t="models.table">Tafel</span>
@tspan = (path, vars={}) -> "<span data-t='#{path}' class='translation' data-t-attributes='#{JSON.stringify(vars)}'>#{t(path, vars)}</span>" @tspan = (path, vars={}) -> "<span data-t='#{path}' class='translation' data-t-attributes='#{JSON.stringify(vars)}'>#{t(path, vars)}</span>"
@@ -4,11 +4,13 @@ App.MenuProductComponent = Ember.Component.extend
specific_id: (-> "order-product-#{@get('product.id')}").property('product.id') specific_id: (-> "order-product-#{@get('product.id')}").property('product.id')
orderProducts: false orderProducts: false
target: -> @get('parentView.targetObject') target: -> @get('parentView.targetObject')
showDescriptionIcon: Ember.computed.or 'product.description', 'product.product_variants.length'
actions: actions:
addProduct: (product)-> addProduct: (product)->
if existing = @target().store.all('product_order').find((po)-> po.get('product') == product and not po.get('order')) if product.get('product_variants.length')
existing.increment() @target().modal 'product_variant_select', model: product
else else
@target().store.createRecord 'product_order', product: product, price: product.get('price') product.addOrderItem()
showProductDescription: (product)-> showProductDescription: (product)->
@target().modal 'product_info', model: product, title: product.get('name') @target().modal 'product_info', model: product, title: product.get('name')
@@ -0,0 +1,5 @@
@App.modals.ProductVariantSelectController = @App.modals.BaseController.extend
actions:
chooseProductVariant: (product_variant)->
@get('model').addOrderItem(product_variant: product_variant.get('name'))
@send 'close'
@@ -31,9 +31,16 @@ App.ProductOrdersController = Ember.ArrayController.extend
#orders = @store.all('product_order').toArray() #orders = @store.all('product_order').toArray()
#data = @get('product_orders').map( (po)->po.serialize() ) #data = @get('product_orders').map( (po)->po.serialize() )
dataObject = order: {table_id: @get('controllers.table.model.id')} dataObject = {table_id: @get('controllers.table.model.id')}
@get('product_orders').forEach (product_order)-> dataObject['order'][product_order.get('product.id')] = product_order.get('quantity') dataObject.product_orders = @get('product_orders').map( (po) -> po.serialize()).toArray()
Ember.$.post "#{$data_host}/user/orders.json", dataObject, (response) => #@get('product_orders').forEach (product_order)-> dataObject['order'][product_order.get('product.id')] = product_order.get('quantity')
Ember.$.ajax
type: 'POST'
dataType: 'json'
contentType: 'application/json'
url: "#{$data_host}/user/orders.json"
data: JSON.stringify(dataObject)
success: (response) =>
@store.pushPayload('order', response) if response.order @store.pushPayload('order', response) if response.order
@transitionToRoute 'active_list' @transitionToRoute 'active_list'
@get('product_orders').invoke 'unloadRecord' @get('product_orders').invoke 'unloadRecord'
@@ -0,0 +1,5 @@
attr = DS.attr
App.ProductVariant = DS.Model.extend
name: attr 'string'
product: DS.belongsTo 'product'
position: attr 'number', defaultValue: 0
@@ -6,5 +6,26 @@ App.Product = DS.Model.extend
position: attr('number', defaultValue: 0) position: attr('number', defaultValue: 0)
active: attr 'boolean', defaultValue: true active: attr 'boolean', defaultValue: true
image: attr() image: attr()
product_category: DS.belongsTo('product_category') product_category: DS.belongsTo('product-category')
product_orders: DS.hasMany('product_order') product_orders: DS.hasMany('product-order')
product_variants: DS.hasMany 'product-variant'
sorted_product_variants: Ember.computed 'product_variants.@each.name', 'product_variants.@each.position', ->
@get('product_variants').sortBy('position')
variantsDisplay: Ember.computed 'sorted_product_variants', ->
@get('sorted_product_variants').mapBy('name').join(', ')
addOrderItem: (options = {})->
#if existing = @store.all('product_order').find((po)-> po.get('product') == product and not po.get('order'))
if options.product_variant
existing = @get('product_orders').find( (po)-> !po.get('order') and po.get('product_variant') is options.product_variant )
else
existing = @get('product_orders').find( (po)-> !po.get('order') )
if existing
existing.increment()
else
@store.createRecord 'product_order',
product: this
price: @get('price')
product_variant: options.product_variant
@@ -4,12 +4,17 @@ App.ProductOrder = DS.Model.extend
price: attr 'number' price: attr 'number'
product_name: attr('string') product_name: attr('string')
product: DS.belongsTo('product') product: DS.belongsTo('product')
product_variant: attr('string')
order: DS.belongsTo('order') order: DS.belongsTo('order')
placed: attr('boolean', defaultValue: false) # virtual attribute for new orders to be placed, should be more elegant placed: attr('boolean', defaultValue: false) # virtual attribute for new orders to be placed, should be more elegant
increment: -> increment: ->
@set('quantity', @get('quantity') + 1) @set('quantity', @get('quantity') + 1)
total: (-> @get('quantity') * @get('price')).property('quantity', 'price') total: (-> @get('quantity') * @get('price')).property('quantity', 'price')
display: (-> "#{@get('quantity')} x #{@get('product.name')}").property('quantity', 'product.name') display: Ember.computed 'quantity', 'product_variant', 'product.name', ->
disp = "#{@get('quantity')} x #{@get('product.name')}"
if variant = @get('product_variant')
disp = "#{disp} (#{variant})"
disp.htmlSafe()
setOrder: (order)-> setOrder: (order)->
@set('placed', true) @set('placed', true)
@set('order', order) @set('order', order)
@@ -1,4 +1,4 @@
if product.description if showDescriptionIcon
button.show-product-description{action "showProductDescription" product} button.show-product-description{action "showProductDescription" product}
span span
else else
@@ -1,5 +1,10 @@
if image if image
.right: img src=image.small alt="" .right: img src=image.small alt=""
p==description p==description
if product_variants
h4= t 'models.plural.product_variant'
ul
each product_variant in product_variants
li= product_variant.name
hr hr
button{action "close"}= t 'modal.info.close' button{action "close"}= t 'modal.info.close'
@@ -0,0 +1,8 @@
if model.image
.right: img src=model.image.small alt=""
each product_variant in product_variants
.row
.small-8.columns: a{action "chooseProductVariant" product_variant}= product_variant.name
.small-4.columns: a.choose-product-variant-button{action "chooseProductVariant" product_variant}= t 'product_variant.choose'
hr
button.modal-close{action "close"}= t 'modal.info.close'
@@ -90,6 +90,15 @@
@extend .fa @extend .fa
@extend .fa-lg @extend .fa-lg
@extend .fa-trash @extend .fa-trash
.remove-product-variant
+push-button($bg: $alert-color, $padding: 5px)
span
@extend .fa, .fa-trash
.add-product-variant
+button($bg: $success-color, $padding: $button-tny)
.icon
@extend .fa, .fa-plus
margin-right: 6px
.error .error
color: $alert-color color: $alert-color
input input
@@ -1,7 +1,8 @@
.modal .modal
margin: auto margin: auto
margin-top: 90px margin-top: 90px
width: 300px width: 90%
max-width: 756px
background-color: #fff background-color: #fff
padding: 1em padding: 1em
z-index: 9085 z-index: 9085
@@ -0,0 +1,4 @@
.choose-product-variant-button
+button($padding: $button-tny)
margin: 0
margin-bottom: 6px
@@ -0,0 +1,66 @@
module Suppliers
class ProductVariantsController < Suppliers::ApplicationController
layout 'tablet'
# GET /product_variants
# GET /product_variants.json
def index
end
# GET /product_variants/1
# GET /product_variants/1.json
def show
end
# GET /product_variants/new
# GET /product_variants/new.json
def new
end
# GET /product_variants/1/edit
def edit
end
# POST /product_variants
# POST /product_variants.json
def create
@product_variant = ProductVariant.new(product_variant_params)
@product_variant.supplier = current_supplier
if @product_variant.save
render json: @product_variant
else
render json: {errors: @product_variant.errors}, status: :unprocessable_entity
end
end
# PUT /product_variants/1
# PUT /product_variants/1.json
def update
@product_variant = ProductVariant.find(params[:id])
head :unprocessable_entity and return unless product_id = product_variant_params[:product_id]
product = Product.find(product_id)
head :forbidden and return unless product.supplier_id == current_supplier.id
if @product_variant.update_attributes(product_variant_params)
render json: @product_variant
else
render json: {errors: @product_variant.errors}, status: :unprocessable_entity
end
end
# DELETE /product_variants/1
# DELETE /product_variants/1.json
def destroy
#@product_variant = ProductVariant.find_by_supplier_id_and_id!(current_supplier.id, params[:id])
@product_variant = ProductVariant.find(params[:id])
@product_variant.destroy
render json: {}
end
private
def product_variant_params
params.require(:product_variant).permit(:name, :product_id)
end
end
end
@@ -5,7 +5,7 @@ module Suppliers
end end
def show def show
[current_supplier].include_relations(sections: :tables, product_categories: :products) [current_supplier].include_relations(sections: :tables, product_categories: {products: :product_variants})
render json: current_supplier, serializer: Suppliers::SupplierSerializer #.new(current_supplier).as_json render json: current_supplier, serializer: Suppliers::SupplierSerializer #.new(current_supplier).as_json
end end
+3 -6
View File
@@ -6,9 +6,6 @@ module Users
# POST /user/orders # POST /user/orders
def create def create
# render json: {}, status: :unprocessable_entity and return unless params[:order].present? && params[:order][:product_orders].present? # render json: {}, status: :unprocessable_entity and return unless params[:order].present? && params[:order][:product_orders].present?
# converted_order = params[:order][:product_orders].each_with_object({}){|po, o| o[po[:product_id]] = po[:quantity] }
converted_order = params[:order]
table_id = params[:order].delete('table_id')
if list = current_user.active_list if list = current_user.active_list
render json: {}, status: :not_acceptable and return unless list.supplier.open? render json: {}, status: :not_acceptable and return unless list.supplier.open?
else else
@@ -16,8 +13,8 @@ module Users
#NOTE: security bug here!!!!!! #NOTE: security bug here!!!!!!
# - supplier.open? # - supplier.open?
# - etc.... # - etc....
render json: {}, status: :unprocessable_entity and return unless table_id.present? render json: {}, status: :unprocessable_entity and return unless params[:table_id].present?
table = Table.find(table_id) table = Table.find(params[:table_id])
render json: {}, status: :not_acceptable and return unless table.supplier.open? render json: {}, status: :not_acceptable and return unless table.supplier.open?
if table.occupied? if table.occupied?
@@ -27,7 +24,7 @@ module Users
list = List.from_table( table, current_user ) list = List.from_table( table, current_user )
end end
order = list.place_order products: converted_order, user: current_user order = list.place_order product_orders: params[:product_orders], user: current_user
render json: order, serializer: OrderSerializer render json: order, serializer: OrderSerializer
#render nothing: true #render nothing: true
end end
+10 -6
View File
@@ -9,7 +9,10 @@ class Employee
} }
DEFAULT_SETTINGS.each do |attribute, default_value| DEFAULT_SETTINGS.each do |attribute, default_value|
define_method(attribute) { settings.public_send attribute } define_method(attribute) { settings.public_send attribute }
define_method("#{attribute}=") { |value| settings.set attribute, value } define_method("#{attribute}=") do |value|
is_dirty
settings.set attribute, value
end
if default_value == true or default_value == false # boolean if default_value == true or default_value == false # boolean
define_method(:"#{attribute}?"){ public_send attribute } define_method(:"#{attribute}?"){ public_send attribute }
end end
@@ -44,11 +47,12 @@ class Employee
end end
alias_method :settings=, :enrich_with_settings alias_method :settings=, :enrich_with_settings
alias_method :orig_save, :save #alias_method :orig_save, :save
def save(*args) #def save(*args)
settings.persist! #settings.persist!
orig_save(*args) #orig_save(*args)
end #end
before_save { settings.persist! }
def settings def settings
@settings || SupplierEmployeesSettings.new(Supplier.new).for_employee(self) @settings || SupplierEmployeesSettings.new(Supplier.new).for_employee(self)
+20 -7
View File
@@ -17,6 +17,7 @@ class List
belongs_to :table belongs_to :table
belongs_to :supplier belongs_to :supplier
belongs_to :section belongs_to :section
has_many :list_payments
has_and_belongs_to_many :users, storing_keys: true has_and_belongs_to_many :users, storing_keys: true
has_and_belongs_to_many :employees, storing_keys: true has_and_belongs_to_many :employees, storing_keys: true
@@ -274,16 +275,28 @@ class List
state == 'active' state == 'active'
end end
def place_order(products: {}, user: nil, employee: nil) def closed?
return false unless products.any? state == 'closed'
end
def place_order(product_orders: [], user: nil, employee: nil)
return false unless product_orders.any?
order = Order.create list: self, supplier: supplier, user: user, employee: employee, section_id: section_id, table_id: table_id order = Order.create list: self, supplier: supplier, user: user, employee: employee, section_id: section_id, table_id: table_id
return unless order.id return unless order.id
orders_placed_count = supplier.increment_orders_placed_count! orders_placed_count = supplier.increment_orders_placed_count!
loaded_products = self.class.database.load_document products.keys loaded_products = self.class.database.load_document product_orders.map{|po| po['product_id']}
products.each do |product_id, quantity| product_orders.each do |product_order|
product = loaded_products.find{|p| p.id == product_id} # to get the price next unless product = loaded_products.find{|p| p.id == product_order['product_id']} # to get the price
if quantity.to_i > 0 quantity = product_order['quantity'].to_i
ProductOrder.create(order: order, product_id: product_id, quantity: quantity, price: product.price, product_name: product.name) if quantity > 0
ProductOrder.create(
order: order,
product_id: product.id,
quantity: quantity,
price: product.price,
product_name: product.name,
product_variant: product_order['product_variant']
)
end end
end end
set_price set_price
+16
View File
@@ -0,0 +1,16 @@
class ListPayment
include SimplyStored::Couch
include ActiveModel::SerializerSupport
property :amount
property :source # bitcoin, apple_pay, etc...
belongs_to :list
belongs_to :user
after_save :close_list_when_is_paid
validates :list_id, presence: true
def close_list_when_is_paid
amount_paid = list.list_payments.inject(0.0){|sum, payment| sum + payment.amount}
list.close! if amount_paid >= list.price
end
end
+1
View File
@@ -15,6 +15,7 @@ class Product
#has_and_belongs_to_many :product_categories, storing_keys: false #has_and_belongs_to_many :product_categories, storing_keys: false
belongs_to :supplier # direct! category is an aid belongs_to :supplier # direct! category is an aid
has_many :product_orders has_many :product_orders
has_many :product_variants, dependent: :destroy
attr_protected :supplier_id attr_protected :supplier_id
+3 -1
View File
@@ -5,6 +5,7 @@ class ProductOrder
property :quantity, type: Fixnum property :quantity, type: Fixnum
property :price, type: Float property :price, type: Float
property :product_name property :product_name
property :product_variant
belongs_to :product belongs_to :product
belongs_to :order belongs_to :order
@@ -14,7 +15,8 @@ class ProductOrder
# Getter for product name. If a supplier deletes a product, that has product_orders, the product # Getter for product name. If a supplier deletes a product, that has product_orders, the product
# will become nil. This method should handle this case. # will become nil. This method should handle this case.
alias_method :direct_product_name, :product_name
def product_name def product_name
product.try(:name) || '[deleted]' direct_product_name.presence || product.try(:name) || '[deleted]'
end end
end end
+8
View File
@@ -0,0 +1,8 @@
class ProductVariant
include SimplyStored::Couch
include ActiveModel::SerializerSupport
property :name
property :position, type: Fixnum, default: 0
belongs_to :product
belongs_to :supplier
end
+5
View File
@@ -20,6 +20,10 @@ class Supplier
property :week_starts_on_monday, type: :boolean, default: true property :week_starts_on_monday, type: :boolean, default: true
property :employee_settings_storage property :employee_settings_storage
# PAYMENT
property :accept_bitpay, type: :boolean, default: false
property :bitpay_number
#LOCATION #LOCATION
property :lat, type: Float #, default: 52.08062426379751 property :lat, type: Float #, default: 52.08062426379751
property :lng, type: Float #, default: 4.312562942504883 property :lng, type: Float #, default: 4.312562942504883
@@ -35,6 +39,7 @@ class Supplier
#has_many :orders, through: :lists #has_many :orders, through: :lists
has_many :products, dependent: :destroy has_many :products, dependent: :destroy
has_many :product_variants
has_many :product_categories, dependent: :destroy has_many :product_categories, dependent: :destroy
has_many :tables, dependent: :destroy has_many :tables, dependent: :destroy
has_many :lists, dependent: :destroy has_many :lists, dependent: :destroy
+1
View File
@@ -19,6 +19,7 @@ class User
has_and_belongs_to_many :lists, storing_keys: false has_and_belongs_to_many :lists, storing_keys: false
has_many :orders has_many :orders
has_many :list_payments
validates_uniqueness_of :email validates_uniqueness_of :email
before_save :ensure_authentication_token before_save :ensure_authentication_token
+1 -1
View File
@@ -1,4 +1,4 @@
# Used for user ember1 # Used for user ember1
class ProductOrderSerializer < Qwaiter::Serializer class ProductOrderSerializer < Qwaiter::Serializer
attributes :order_id, :product_id, :quantity, :price, :product_name attributes :order_id, :product_id, :quantity, :price, :product_name, :product_variant
end end
+3
View File
@@ -1,6 +1,9 @@
class ProductSerializer < Qwaiter::Serializer class ProductSerializer < Qwaiter::Serializer
root 'product'
attributes :name, :price, :description, :image, :code, :position, :visible, :active, :product_category_id attributes :name, :price, :description, :image, :code, :position, :visible, :active, :product_category_id
has_many :product_variants
def image def image
if object.image.present? if object.image.present?
{small: object.image.url(:small)} {small: object.image.url(:small)}
@@ -0,0 +1,4 @@
class ProductVariantSerializer < Qwaiter::Serializer
root 'product_variant'
attributes :name
end
+2
View File
@@ -78,3 +78,5 @@ en:
selected_products: selected_products:
clear: Clear clear: Clear
order: Order order: Order
product_variant:
add_product_variant: Add ${models.product_variant}
+4
View File
@@ -9,6 +9,7 @@ en:
product: Product product: Product
order: Order order: Order
product_category: Product category product_category: Product category
product_variant: Variant
section: Section section: Section
join_request: Join request join_request: Join request
user_feedback: User feedback user_feedback: User feedback
@@ -26,6 +27,7 @@ en:
product: Products product: Products
order: Orders order: Orders
product_category: Product categories product_category: Product categories
product_variant: Variants
section: Sections section: Sections
join_request: Join requests join_request: Join requests
user_feedback: User feedbacks user_feedback: User feedbacks
@@ -52,6 +54,8 @@ en:
visible: Visible? visible: Visible?
created_at: Created created_at: Created
image: Image image: Image
product_variant:
name: Name
list: list:
created_at: Created created_at: Created
state: Status state: Status
+4
View File
@@ -9,6 +9,7 @@ nl:
product: Product product: Product
order: Bestelling order: Bestelling
product_category: Product categorie product_category: Product categorie
product_variant: Variant
section: Afdeling section: Afdeling
join_request: Deelname verzoek join_request: Deelname verzoek
employee: Werknemer employee: Werknemer
@@ -25,6 +26,7 @@ nl:
product: Producten product: Producten
order: Bestellingen order: Bestellingen
product_category: Product categorieen product_category: Product categorieen
product_variant: Varianten
section: Afdelingen section: Afdelingen
join_request: Deelname verzoeken join_request: Deelname verzoeken
employee: Werknemers employee: Werknemers
@@ -50,6 +52,8 @@ nl:
active: "Actief?" active: "Actief?"
created_at: Aangemaakt created_at: Aangemaakt
image: Afbeelding image: Afbeelding
product_variant:
name: Naam
list: list:
created_at: Aangemaakt created_at: Aangemaakt
state: Status state: Status
+2
View File
@@ -76,6 +76,8 @@ nl:
selected_products: selected_products:
clear: Leegmaken clear: Leegmaken
order: Bestellen order: Bestellen
product_variant:
add_product_variant: ${models.product_variant} toevoegen
views: views:
pagination: pagination:
first: "&laquo; Eerste" first: "&laquo; Eerste"
+1
View File
@@ -139,6 +139,7 @@ Qwaiter::Application.routes.draw do
get :preview_products get :preview_products
end end
end end
resources :product_variants
resources :lists do resources :lists do
collection do collection do
get :active get :active
-12183
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,7 +1,8 @@
db: db:
image: bterkuile/couchdb image: bterkuile/couchdb
volumes: volumes:
- db:/usr/local/var/lib/couchdb - db/data:/usr/local/var/lib/couchdb
- db/config:/usr/local/etc/couchdb
expose: expose:
- 5984 - 5984
net: host net: host
+1 -1
View File
@@ -20,5 +20,5 @@ step 'a new order on a table in another section is created' do
# @new_section = create :section, title: 'Terrace', supplier: @supplier # @new_section = create :section, title: 'Terrace', supplier: @supplier
# @new_table = create :table, number: 59, section: @new_section, supplier: @supplier # @new_table = create :table, number: 59, section: @new_section, supplier: @supplier
@new_list = create :list, section: @other_section, table: @other_table, supplier: @supplier, user_ids: [@user.id] @new_list = create :list, section: @other_section, table: @other_table, supplier: @supplier, user_ids: [@user.id]
@new_order = @new_list.place_order(products: {@product.id => 3}, user: @user) @new_order = @new_list.place_order(product_orders: [ {'product_id' => @product.id, 'quantity' => 3}], user: @user)
end end
+1 -1
View File
@@ -1,7 +1,7 @@
step "A new order is placed" do step "A new order is placed" do
@user ||= create :user @user ||= create :user
@list = create :list, state: 'active', supplier: @supplier, table: @table, section: @section, user_ids: [@user.id] @list = create :list, state: 'active', supplier: @supplier, table: @table, section: @section, user_ids: [@user.id]
@order = @list.place_order products: {@product.id => 2}, user: @user @order = @list.place_order product_orders: [{ 'product_id' => @product.id, 'quantity' => 2}], user: @user
end end
step "an order with :quantity products :product_name should have been created" do |quantity, product_name| step "an order with :quantity products :product_name should have been created" do |quantity, product_name|
+1 -1
View File
@@ -9,7 +9,7 @@ step "the order should be marked as delivered" do
end end
step "another order is placed" do step "another order is placed" do
@new_order = @list.place_order(products: {@product.id => 5}, user: @user) @new_order = @list.place_order(product_orders: [{ 'product_id' => @product.id, 'quantity' => 5}], user: @user)
end end
step "the user order should be created as a new order" do step "the user order should be created as a new order" do
@@ -43,7 +43,7 @@ step "there is another signed in user on the same list" do
end end
step 'the other user orders a product :count times' do |count| step 'the other user orders a product :count times' do |count|
@list.place_order products: {@product.id => count.to_i}, user: @other_user @list.place_order product_orders: [{'product_id' => @product.id, 'quantity' => count.to_i}], user: @other_user
end end
step 'the other user is part of the list' do step 'the other user is part of the list' do
+5
View File
@@ -0,0 +1,5 @@
FactoryGirl.define do
factory :list_payment do
end
end
+7
View File
@@ -0,0 +1,7 @@
Logo-bigMozo.bar is een systeem om bestellingen te doen met je smartphone. Als je wel eens op een terras of een afgelegen deel van een horeca gelegenheid hebt gezeten en vond dat het te lang duurde voordat er iemand aan je kwam vragen wat je wil bestellen dan weet je dat er nog veel te verbeteren is.
Om horeca gelegenheden te helpen de vraag van klanten beter te kunnen verwerken is mozo.bar ontstaan. Mozo.bar is een toevoeging voor horeca gelegenheden om klanten met hun smartphone te laten bestellen. Dit is de focus van Mozo.bar! Om het gemak en de sfeer waar mensen aan gewend zijn te handhaven integreert Mozo.bar zo goed mogelijk met de ervaring van mensen. Klanten kunnen gewoon op een lijstje bestellen en bij ondersteunde systemen komt de bestelling gewoon achter de bar of in de keuken uitgeprint.
Mozo.bar is gratis om in gebruik te nemen en in een handomdraai toegevoegd als extra dienst. Maak als horeca gelegenheid een account aan. Richt deze in en je kan aan de slag!
Heb je een ervaring of idee waardoor we de ervaring voor zowel de horeca bezoeker als de horeca gelegenheid kunnen verbeteren mail dit dan naar: experience@mozo.bar dan kunnen wij ervoor zorgen dat het gaan naar een horeca gelegenheid leuk en makkelijk wordt!
+4
View File
@@ -0,0 +1,4 @@
<div style="position: absolute; right: 0; top: -170px; z-index: 2222;"><img src="/assets/site/tablet-phone.png" alt="" width="249" height="166" /></div>
<p><img class="home-qr" src="/assets/qr.png" alt="Qr" width="124" height="124" />Welkom bij de mozo.bar&nbsp;website! Mozo.bar&nbsp;is een mobiele app waarmee je gemakkelijk bestellingen kan doen op terrassen, in bars of restaurants. Scan een Qr code op de tafel, en krijg meteen het menu te zien. Klik aan wat je wilt hebben en bestel. Dat is alles!</p>
<p>Momenteel is mozo.bar&nbsp;in de ontwikkelingsfase. Dit houdt in dat er nog veel moet gebeuren en dat we volledig open staan voor alle input die we kunnen krijgen. Onze missie is om zowel klant als kroegeigenaar blij te maken met de mogelijkheden die deze tijd ons biedt.</p>
<p>Schrijf je in op&nbsp;<a style="color: #7bb459; text-decoration: none;" href="https://www.facebook.com/mozo.bar" target="_blank">Facebook</a>&nbsp;of&nbsp;<a style="color: #7bb459; text-decoration: none;" href="https://www.twitter.com/Qwaiter" target="_blank">Twitter</a>&nbsp;om op de hoogte te worden gehouden.</p>
@@ -0,0 +1,13 @@
moduleForModel 'product', 'Product model',
needs: [
'model:product_variant',
'model:product_category',
'model:section_area',
'model:product_order',
'model:order',
'model:supplier'
]
#test "Nothing yet", ->
#element = @subject()
#assert 2, 2
@@ -1,5 +1,5 @@
moduleForModel 'section-element', 'Section element model', moduleForModel 'section-element', 'Section element model',
needs: ['model:section', 'model:table'] needs: ['model:section', 'model:table', 'model:section-area']
test "box_size", -> test "box_size", ->
element = @subject element = @subject
+21
View File
@@ -0,0 +1,21 @@
require 'spec_helper'
describe ListPayment do
it "does completes a list if amount is equal to the list amount" do
list = create :list, price: 10.22
create :list_payment, amount: 10.22, list: list
list.should be_closed
end
it "does completes a list if amount is greater than the list amount" do
list = create :list, price: 10.22
create :list_payment, amount: 12, list: list
list.should be_closed
end
it "does completes a list if amount is less than the list amount, but total payments on list bigger" do
list = create :list, price: 10.22
create :list_payment, amount: 7, list: list
list.should be_active
create :list_payment, amount: 7, list: list
list.should be_closed
end
end
+6 -6
View File
@@ -126,30 +126,30 @@ describe List do
describe '#place_order' do describe '#place_order' do
it 'returns an order object' do it 'returns an order object' do
list.place_order(products: {product.id => 7}, user: user).should be_a Order list.place_order(product_orders: [{'product_id' => product.id, 'quantity' => 7}], user: user).should be_a Order
end end
it 'creates an order' do it 'creates an order' do
expect{ list.place_order(products: {product.id => 7}, user: user) }.to change{ Order.count }.by(1) expect{ list.place_order(product_orders: [{'product_id' => product.id, 'quantity' => 7}], user: user) }.to change{ Order.count }.by(1)
end end
describe 'broadcasting' do describe 'broadcasting' do
it 'broadcasts to the user and the supplier the active order counter' do it 'broadcasts to the user and the supplier the active order counter' do
# create existing order # create existing order
list.place_order(products: {product.id => 7}, user: user) list.place_order(product_orders: [{'product_id' => product.id, 'quantity' => 7}], user: user)
# expect{ # expect{
# list.place_order(products: {product.id => 3}, user: user) # list.place_order(product_orders: [{product_id: product.id, quantity: 5}], user: user)
# }.to broadcast_to_user(user.id).message('orders_placed_count').with(count: 2) # }.to broadcast_to_user(user.id).message('orders_placed_count').with(count: 2)
expect{ expect{
list.place_order(products: {product.id => 5}, user: user) list.place_order(product_orders: [{'product_id' => product.id, 'quantity' => 5}], user: user)
}.to broadcast_to_supplier(supplier.id).message('orders_placed_count').with(count: 2) }.to broadcast_to_supplier(supplier.id).message('orders_placed_count').with(count: 2)
end end
end end
it 'sets the list price as kind of caching' do it 'sets the list price as kind of caching' do
list.place_order(products: {product.id => 7}, user: user) list.place_order(product_orders: [{'product_id' => product.id, 'quantity' => 7}], user: user)
list.reload list.reload
list.price.should == 15.54 list.price.should == 15.54
end end
+1
View File
@@ -106,6 +106,7 @@ RSpec.configure do |config|
config.infer_base_class_for_anonymous_controllers = true config.infer_base_class_for_anonymous_controllers = true
config.filter_run_excluding broken: true config.filter_run_excluding broken: true
config.render_views = true config.render_views = true
config.expect_with(:rspec) { |c| c.syntax = [:expect, :should] }
OmniAuth.config.test_mode = true OmniAuth.config.test_mode = true
OmniAuth.config.add_mock :facebook, { OmniAuth.config.add_mock :facebook, {
+8
View File
@@ -103,3 +103,11 @@ Integrations
- Bonnetjes machine - Bonnetjes machine
- Payleven? Payment - Payleven? Payment
Braindump:
Create blog
Explanation about ordering
The price is the actual price at the restaurant, is different when
waited for a long time