Compare commits
26 commits
demo-merge
...
demo
Author | SHA1 | Date | |
---|---|---|---|
|
eb6cf00f94 | ||
c76c148f99 | |||
ce7b4d7ce4 | |||
|
eb719057c4 | ||
|
2614f095cb | ||
b94ca21022 | |||
|
8cb86b2f88 | ||
3d71d266e3 | |||
ee03a2a9af | |||
|
237ef5d38b | ||
|
dfe8beae2c | ||
|
75bb400d0d | ||
6f2a3b4f5f | |||
d81ae10dc8 | |||
4b5775e107 | |||
|
936c1ba878 | ||
|
b3571515b0 | ||
|
28c851823a | ||
|
25d4efa71a | ||
|
49a04b226c | ||
|
69c80eba3e | ||
|
e6e2cdc2c6 | ||
4bb724495d | |||
0bd04fba41 | |||
46e3794a4e | |||
5c04a43f61 |
40
.drone.yml
|
@ -79,7 +79,7 @@ steps:
|
||||||
- name: deployment
|
- name: deployment
|
||||||
image: git.local-it.org/philipp/stack-ssh-deply:latest
|
image: git.local-it.org/philipp/stack-ssh-deply:latest
|
||||||
settings:
|
settings:
|
||||||
stack: "foodsoft_${DRONE_COMMIT:0:8}"
|
stack: "foodsoft_${DRONE_BRANCH}"
|
||||||
compose: "deployment/compose.yml"
|
compose: "deployment/compose.yml"
|
||||||
deploy_key:
|
deploy_key:
|
||||||
from_secret: drone_deploy_key
|
from_secret: drone_deploy_key
|
||||||
|
@ -96,23 +96,23 @@ steps:
|
||||||
- proxy
|
- proxy
|
||||||
environment:
|
environment:
|
||||||
IMAGE: git.local-it.org/foodsoft/foodsoft:${DRONE_COMMIT:0:8}
|
IMAGE: git.local-it.org/foodsoft/foodsoft:${DRONE_COMMIT:0:8}
|
||||||
STACK_NAME: "foodsoft_${DRONE_COMMIT:0:8}"
|
STACK_NAME: "foodsoft_${DRONE_BRANCH}"
|
||||||
DOMAIN: "${DRONE_COMMIT:0:8}.foodsoft.dev.local-it.cloud"
|
DOMAIN: "foodsoft.dev.local-it.cloud"
|
||||||
LETS_ENCRYPT_ENV: production
|
LETS_ENCRYPT_ENV: production
|
||||||
FOODCOOP_MULTI_INSTALL: true
|
FOODCOOP_MULTI_INSTALL: true
|
||||||
FOODCOOP_NAME: example
|
FOODCOOP_NAME: Einkaufskooperative Foobar
|
||||||
FOODCOOP_CITY: XXX
|
FOODCOOP_CITY: Berlin
|
||||||
FOODCOOP_COUNTRY: XXX
|
FOODCOOP_COUNTRY: Deutschland
|
||||||
FOODCOOP_EMAIL: info@example.org
|
FOODCOOP_EMAIL: foodsoft@local-it.org
|
||||||
FOODCOOP_PHONE: XXX
|
FOODCOOP_PHONE: 123456789
|
||||||
FOODCOOP_STREET: XXX
|
FOODCOOP_STREET: Einkaufsstraße 5
|
||||||
FOODCOOP_ZIP_CODE: XXX
|
FOODCOOP_ZIP_CODE: 12345
|
||||||
FOODCOOP_HOMEPAGE: https://order.example.org
|
FOODCOOP_HOMEPAGE: https://foodsoft.local-it.org
|
||||||
FOODCOOP_HELP_URL: https://order.example.org
|
FOODCOOP_HELP_URL: https://git.local-it.org/foodsoft/foodsoft
|
||||||
FOODCOOP_TIME_ZONE: Berlin
|
FOODCOOP_TIME_ZONE: Berlin
|
||||||
FOODCOOP_USE_NICK: true
|
FOODCOOP_USE_NICK: true
|
||||||
FOODCOOP_LANGUAGE: de
|
FOODCOOP_LANGUAGE: de
|
||||||
FOODCOOP_FOOTER: '<a href="https://example.org/">example</a> hosted by <a href="https://yourhoster.org">Your Tech Co-op</a>.'
|
FOODCOOP_FOOTER: '<a href="https://foodsoft.local-it.org/">Foodsoft</a> hosted by <a href="https://local-it.org">local-it e,V,</a>.'
|
||||||
USE_APPLE_POINTS: false
|
USE_APPLE_POINTS: false
|
||||||
STOP_ORDERING_UNDER: 75
|
STOP_ORDERING_UNDER: 75
|
||||||
MINIMUM_BALANCE: 0
|
MINIMUM_BALANCE: 0
|
||||||
|
@ -120,15 +120,15 @@ steps:
|
||||||
MYSQL_HOST: db
|
MYSQL_HOST: db
|
||||||
MYSQL_PORT: 3306
|
MYSQL_PORT: 3306
|
||||||
MYSQL_USER: foodsoft
|
MYSQL_USER: foodsoft
|
||||||
EMAIL_SENDER: noreply@example.org
|
EMAIL_SENDER: demo@local-it.org
|
||||||
EMAIL_ERROR: systems@example.org
|
EMAIL_ERROR: flip@yksflip.de
|
||||||
SMTP_ADDRESS: mail.example.com
|
SMTP_ADDRESS: mail.local-it.org
|
||||||
SMTP_AUTHENTICATION: plain
|
SMTP_AUTHENTICATION: login
|
||||||
SMTP_DOMAIN: mail.example.com
|
SMTP_DOMAIN: mail.local-it.org
|
||||||
SMTP_ENABLE_STARTTLS_AUTO: true
|
SMTP_ENABLE_STARTTLS_AUTO: true
|
||||||
SMTP_PORT: 587
|
SMTP_PORT: 587
|
||||||
SMTP_USER_NAME: foodsoft
|
SMTP_USER_NAME: demo@local-it.org
|
||||||
EMAIL_REPLY_DOMAIN: example.org
|
EMAIL_REPLY_DOMAIN:
|
||||||
SMTP_SERVER_HOST: 0.0.0.0
|
SMTP_SERVER_HOST: 0.0.0.0
|
||||||
SMTP_SERVER_PORT: 2525
|
SMTP_SERVER_PORT: 2525
|
||||||
SECRET_DB_PASSWORD_VERSION: v1
|
SECRET_DB_PASSWORD_VERSION: v1
|
||||||
|
|
2
Gemfile
|
@ -23,7 +23,7 @@ gem 'bootsnap', require: false
|
||||||
gem 'mysql2'
|
gem 'mysql2'
|
||||||
gem 'prawn'
|
gem 'prawn'
|
||||||
gem 'prawn-table'
|
gem 'prawn-table'
|
||||||
gem 'haml'
|
gem 'haml', '~> 5.0'
|
||||||
gem 'haml-rails'
|
gem 'haml-rails'
|
||||||
gem 'kaminari'
|
gem 'kaminari'
|
||||||
gem 'simple_form'
|
gem 'simple_form'
|
||||||
|
|
|
@ -242,9 +242,8 @@ GEM
|
||||||
rails (>= 4.0.0)
|
rails (>= 4.0.0)
|
||||||
globalid (1.0.0)
|
globalid (1.0.0)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
haml (6.1.1)
|
haml (5.2.2)
|
||||||
temple (>= 0.8.2)
|
temple (>= 0.8.0)
|
||||||
thor
|
|
||||||
tilt
|
tilt
|
||||||
haml-rails (2.1.0)
|
haml-rails (2.1.0)
|
||||||
actionpack (>= 5.1)
|
actionpack (>= 5.1)
|
||||||
|
@ -640,7 +639,7 @@ DEPENDENCIES
|
||||||
foodsoft_polls!
|
foodsoft_polls!
|
||||||
foodsoft_wiki!
|
foodsoft_wiki!
|
||||||
gaffe
|
gaffe
|
||||||
haml
|
haml (~> 5.0)
|
||||||
haml-rails
|
haml-rails
|
||||||
hashie (~> 3.4.6)
|
hashie (~> 3.4.6)
|
||||||
i18n-js (~> 3.0.0.rc8)
|
i18n-js (~> 3.0.0.rc8)
|
||||||
|
|
161
README.md
|
@ -1,65 +1,124 @@
|
||||||
Foodsoft
|
Foodsoft
|
||||||
=========
|
=========
|
||||||
[![Build Status](https://github.com/foodcoops/foodsoft/workflows/Ruby/badge.svg)](https://github.com/foodcoops/foodsoft/actions)
|
|
||||||
[![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.svg?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master)
|
|
||||||
[![Docs Status](https://inch-ci.org/github/foodcoops/foodsoft.svg?branch=master)](http://inch-ci.org/github/foodcoops/foodsoft)
|
|
||||||
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.svg)](https://codeclimate.com/github/foodcoops/foodsoft)
|
|
||||||
[![Docker Status](https://img.shields.io/docker/cloud/build/foodcoops/foodsoft.svg)](https://hub.docker.com/r/foodcoops/foodsoft)
|
|
||||||
[![Documentation](https://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/foodcoops/foodsoft)
|
|
||||||
|
|
||||||
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
[Website](https://foodsoft.local-it.org)
|
||||||
|
[Prototypefund](https://prototypefund.de/project/weiterentwicklung-von-foodsoft/)
|
||||||
A food cooperative is a group of people that buy food from suppliers of their own choosing. A collective do-it-yourself supermarket. Members order their products online and collect them on a specified day. And all put in a bit of work to make that possible. Foodsoft facilitates the process.
|
|
||||||
|
|
||||||
If you're a food coop considering to use foodsoft, please have a look at the [wiki page for foodcoops](https://github.com/foodcoops/foodsoft/wiki/For-foodcoops). When you'd like to experiment with or develop foodsoft, you can read [how to set it up](https://github.com/foodcoops/foodsoft/blob/master/doc/SETUP_DEVELOPMENT.md) on your own computer.
|
|
||||||
|
|
||||||
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).
|
|
||||||
|
|
||||||
|
|
||||||
Developing
|
Foodsoft ist ein Tool für [Lebensmittelkooperativen](https://de.wikipedia.org/wiki/Lebensmittelkooperative), welches selbstorganisierte gemeinsame Bestellungen in Großmengen von regionalen und ökologischen Produkten vereinfacht und transparent gestaltet.
|
||||||
----------
|
|
||||||
|
|
||||||
Get foodsoft [running locally](doc/SETUP_DEVELOPMENT.md),
|
Foodsoft wurde ursprünglich entwickelt und betrieben von [foodcoops.net](https://foodcoops.net/)
|
||||||
then visit our [Developing Guidelines](https://github.com/foodcoops/foodsoft/wiki/Developing-Guidelines)
|
|
||||||
page on the wiki.
|
|
||||||
|
|
||||||
Get a foodsoft dev-environment running in the browser with Gitpod
|
|
||||||
|
|
||||||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/foodcoops/foodsoft)
|
|
||||||
|
|
||||||
Follow these [instructions](doc/SETUP_DEVELOPMENT_GITPOD.md) to complete setup from within the Gitpod workspace.
|
|
||||||
|
|
||||||
Deploying
|
|
||||||
---------
|
|
||||||
|
|
||||||
Setup foodsoft to [run in production](doc/SETUP_PRODUCTION.md), or join an existing
|
|
||||||
[hosting platform](https://foodcoops.net/foodsoft-hosting/).
|
|
||||||
|
|
||||||
|
|
||||||
License
|
#### Zielgruppe
|
||||||
-------
|
|
||||||
|
|
||||||
Foodsoft is licensed under the [AGPL](https://www.gnu.org/licenses/agpl-3.0.html)
|
Unsere Zielgruppen sind Bürger:innen, Gruppen und Vereine, die eine Einkauskooperative aufbauen wollen und eine Software, die die Bestellung, Verteilung und Abrechnung erleichtert, benötigen.
|
||||||
license (version 3 or later). Practically this means that you are free to use,
|
|
||||||
adapt and redistribute the software, as long as you publish any changes you
|
|
||||||
make to the code.
|
|
||||||
|
|
||||||
For private use, there are no restrictions, but if you give others access to
|
#### Vorhaben
|
||||||
Foodsoft (like running it open to the internet), you must also make your
|
|
||||||
changes available under the same license. This can be as easy as
|
|
||||||
[forking](https://github.com/foodcoops/foodsoft/fork) the project on Github and
|
|
||||||
pushing your changes. You are not required to integrate your changes back into
|
|
||||||
the main Foodsoft version (but if you're up for it that would be very welcome).
|
|
||||||
|
|
||||||
To make it a little easier, configuration files are exempt, so you can just
|
* ✅ Technische Schuld reduzieren
|
||||||
install and configure Foodsoft without having to publish your changes. These
|
* ✅ Ruby on Rails Upgrade
|
||||||
files are marked as public domain in the file header.
|
* ✅ Artikel Import verbessern
|
||||||
|
(Großhandelschnitstelle)
|
||||||
|
* ✅ Userexperience Verbessern
|
||||||
|
|
||||||
|
#### Was ist eine Einkaufskooperative?
|
||||||
|
|
||||||
|
![Wie funktioniert eine Einkauskooperative?](./doc/foodcoop-explained.jpg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
State of this Fork
|
||||||
|
------------------
|
||||||
|
|
||||||
|
#### Increase Test Coverage
|
||||||
|
|
||||||
|
1. integration and model tests
|
||||||
|
* [x] fork
|
||||||
|
* [x] upstream [#966](https://github.com/foodcoops/foodsoft/pull/966)
|
||||||
|
1. Controller tests
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/8_increase_test_coverage_controllers)
|
||||||
|
* [ ] upstream [#970](https://github.com/foodcoops/foodsoft/pull/970)
|
||||||
|
|
||||||
|
#### Upgrade
|
||||||
|
|
||||||
|
1. Migrate to RSwag API Tests
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/28_introduce_rswag)
|
||||||
|
* [x] upstream [#969](https://github.com/foodcoops/foodsoft/pull/969)
|
||||||
|
1. Rails v7
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/9_rails_v_7)
|
||||||
|
* [x] upstream [#979](https://github.com/foodcoops/foodsoft/pull/979)
|
||||||
|
disussion [#956](https://github.com/foodcoops/foodsoft/issues/956)
|
||||||
|
1. Javascript Importmap
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/9_rails_v_7_js_importmap)
|
||||||
|
* [x] upstream
|
||||||
|
|
||||||
|
#### Article Order Import/Export
|
||||||
|
|
||||||
|
Updating Articles from large resellers and exporting orders is now much easier!
|
||||||
|
|
||||||
|
1. adds bnn fileformat that is used from large german resellers e.g. naturkost nord
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/11_bnn_import_article_update)
|
||||||
|
[gem](https://git.local-it.org/Foodsoft/foodsoft_article_import)
|
||||||
|
* [ ] upstream
|
||||||
|
1. Import category field
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/56_add_update_of_article_category_to_file_import)
|
||||||
|
* [ ] upstream
|
||||||
|
1. Export order as a custom csv file
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/12_generate_custom_csv_file)
|
||||||
|
* [ ] upstream
|
||||||
|
1. Naturkostnord Plugin
|
||||||
|
* [ ] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/12_nkn_file_plugin)
|
||||||
|
* [ ] upstream
|
||||||
|
|
||||||
|
#### Improve User Experience
|
||||||
|
|
||||||
|
1. Richtext editor for messages. Also allows sending attachements.
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/16_html_message_templates)
|
||||||
|
* [x] upstream
|
||||||
|
1. Show the sum of all order group balances
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/47_finance_ordergroup_sums)
|
||||||
|
* [x] upstream
|
||||||
|
1. UI improvements for group order view
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/uxui_group_order)
|
||||||
|
* [ ] upstream
|
||||||
|
1. Favorites
|
||||||
|
* [ ] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/20_favourites)
|
||||||
|
* [ ] upstream
|
||||||
|
1. Show the per kilo / litre price
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/11_include_kilo_litre_price)
|
||||||
|
* [ ] upstream
|
||||||
|
|
||||||
|
#### Other
|
||||||
|
|
||||||
|
1. Fix broken plugin mechanism
|
||||||
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/downgrade-haml)
|
||||||
|
* [x] upstream
|
||||||
|
|
||||||
|
#### Screenshots
|
||||||
|
|
||||||
|
![rswag](./doc/screenshots/rswag.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![bnn upload](./doc/screenshots/bnn_upload.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![message formatting](./doc/screenshots/message_formatting.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![balance sum](./doc/screenshots/balance_sum.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![custom csv export](./doc/screenshots/custom_csv_export.png)
|
||||||
|
csv export
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![order](./doc/screenshots/order.png)
|
||||||
|
|
||||||
If you have any remaining questions, please
|
|
||||||
[open an issue](https://github.com/foodcoops/foodsoft/issues/new) or open a new
|
|
||||||
topic at the [forum](https://forum.foodcoops.net).
|
|
||||||
|
|
||||||
Please see [LICENSE](LICENSE.md) for the full and authoritative text. Some
|
|
||||||
bundled third-party components have [other licenses](vendor/README.md).
|
|
||||||
|
|
||||||
Thanks to [Icons8](http://icons8.com/) for letting us use their icons.
|
|
||||||
|
|
|
@ -179,17 +179,13 @@ function updateBalance() {
|
||||||
var balance = groupBalance - total;
|
var balance = groupBalance - total;
|
||||||
$('#new_balance').html(I18n.l("currency", balance));
|
$('#new_balance').html(I18n.l("currency", balance));
|
||||||
$('#total_balance').val(I18n.l("currency", balance));
|
$('#total_balance').val(I18n.l("currency", balance));
|
||||||
// determine bgcolor and submit button state according to balance
|
|
||||||
var bgcolor = '';
|
|
||||||
if (balance < minimumBalance) {
|
if (balance < minimumBalance) {
|
||||||
bgcolor = '#FF0000';
|
|
||||||
$('#submit_button').attr('disabled', 'disabled')
|
$('#submit_button').attr('disabled', 'disabled')
|
||||||
|
$('#balance-alert').css('display', 'block')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$('#submit_button').removeAttr('disabled')
|
$('#submit_button').removeAttr('disabled')
|
||||||
}
|
$('#balance-alert').css('display', 'none')
|
||||||
// update bgcolor
|
|
||||||
for (i in itemTotal) {
|
|
||||||
$('#td_price_' + i).css('background-color', bgcolor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,7 @@ table {
|
||||||
margin: .5em 0;
|
margin: .5em 0;
|
||||||
|
|
||||||
input:disabled {
|
input:disabled {
|
||||||
background-color: red; }
|
background-color: gray; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,6 +241,9 @@ table {
|
||||||
tr.order-article:hover .article-info {
|
tr.order-article:hover .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
tr.order-article:focus .article-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#order-footer {
|
#order-footer {
|
||||||
|
@ -275,10 +278,13 @@ tr.order-article .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.order-article:hover .article-info {
|
tr.order-article:focus .article-info {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr.order-article:focus {
|
||||||
|
background-color: #E9E9E9;
|
||||||
|
}
|
||||||
|
|
||||||
// ********* Articles
|
// ********* Articles
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
.list .missing-many td, .list .missing-many:hover td {
|
.missing-many td {
|
||||||
background-color: #ebbebe;
|
background-color: #ffc590aa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list .missing-few td, .list .missing-few:hover td {
|
.missing-many:hover td, .missing-many:focus td {
|
||||||
background-color: #ffee75;
|
background-color: #ffc590;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list .missing-none td, .list .missing-none:hover td {
|
.missing-few td {
|
||||||
background-color: #E4EED6;
|
background-color: #fcf488aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.missing-few:hover td, .missing-few:focus td {
|
||||||
|
background-color: #fcf488;
|
||||||
|
}
|
||||||
|
|
||||||
|
.missing-none td {
|
||||||
|
background-color: #d0f6ffaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.missing-none:hover td, .missing-none:focus td {
|
||||||
|
background-color: #d0f6ff;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class OrdersController < ApplicationController
|
||||||
send_order_pdf @order, params[:document]
|
send_order_pdf @order, params[:document]
|
||||||
end
|
end
|
||||||
format.csv do
|
format.csv do
|
||||||
send_data OrderCsv.new(@order).to_csv, filename: @order.name + '.csv', type: 'text/csv'
|
send_data OrderCsv.new(@order, options= {custom_csv: params[:custom_csv]}).to_csv, filename: @order.name + '.csv', type: 'text/csv'
|
||||||
end
|
end
|
||||||
format.text do
|
format.text do
|
||||||
send_data OrderTxt.new(@order).to_txt, filename: @order.name + '.txt', type: 'text/plain'
|
send_data OrderTxt.new(@order).to_txt, filename: @order.name + '.txt', type: 'text/plain'
|
||||||
|
@ -57,6 +57,19 @@ class OrdersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def custom_csv
|
||||||
|
@order = Order.find(params[:id])
|
||||||
|
@view = (params[:view] || 'default').gsub(/[^-_a-zA-Z0-9]/, '')
|
||||||
|
@partial = case @view
|
||||||
|
when 'default' then 'articles'
|
||||||
|
when 'groups' then 'shared/articles_by/groups'
|
||||||
|
when 'articles' then 'shared/articles_by/articles'
|
||||||
|
else 'articles'
|
||||||
|
end
|
||||||
|
|
||||||
|
render :layout => false
|
||||||
|
end
|
||||||
|
|
||||||
# Page to create a new order.
|
# Page to create a new order.
|
||||||
def new
|
def new
|
||||||
if params[:order_id]
|
if params[:order_id]
|
||||||
|
|
|
@ -53,4 +53,12 @@ module GroupOrdersHelper
|
||||||
return 'missing-many'
|
return 'missing-many'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def price_per_base_unit(article:, price:)
|
||||||
|
quantity_unit = QuantityUnit.parse(article.unit)
|
||||||
|
return nil unless quantity_unit.present?
|
||||||
|
|
||||||
|
scaled_price, base_unit = quantity_unit.scale_price_to_base_unit(price)
|
||||||
|
"#{number_to_currency(scaled_price)}/#{base_unit}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -155,4 +155,16 @@ module OrdersHelper
|
||||||
link_to t('orders.index.action_receive'), receive_order_path(order), class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
|
link_to t('orders.index.action_receive'), receive_order_path(order), class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def custom_csv_collection
|
||||||
|
[
|
||||||
|
OrderArticle.human_attribute_name(:units_to_order),
|
||||||
|
Article.human_attribute_name(:order_number),
|
||||||
|
Article.human_attribute_name(:name),
|
||||||
|
Article.human_attribute_name(:unit),
|
||||||
|
Article.human_attribute_name(:unit_quantity_short),
|
||||||
|
ArticlePrice.human_attribute_name(:price),
|
||||||
|
OrderArticle.human_attribute_name(:total_price)
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@ require 'csv'
|
||||||
|
|
||||||
class OrderCsv < RenderCsv
|
class OrderCsv < RenderCsv
|
||||||
def header
|
def header
|
||||||
|
params = @options[:custom_csv]
|
||||||
|
arr = if params.nil?
|
||||||
[
|
[
|
||||||
OrderArticle.human_attribute_name(:units_to_order),
|
OrderArticle.human_attribute_name(:units_to_order),
|
||||||
Article.human_attribute_name(:order_number),
|
Article.human_attribute_name(:order_number),
|
||||||
|
@ -11,19 +13,49 @@ class OrderCsv < RenderCsv
|
||||||
ArticlePrice.human_attribute_name(:price),
|
ArticlePrice.human_attribute_name(:price),
|
||||||
OrderArticle.human_attribute_name(:total_price)
|
OrderArticle.human_attribute_name(:total_price)
|
||||||
]
|
]
|
||||||
|
else
|
||||||
|
[
|
||||||
|
params[:first],
|
||||||
|
params[:second],
|
||||||
|
params[:third],
|
||||||
|
params[:fourth],
|
||||||
|
params[:fifth],
|
||||||
|
params[:sixth],
|
||||||
|
params[:seventh]
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def data
|
||||||
@object.order_articles.ordered.includes([:article, :article_price]).all.map do |oa|
|
@object.order_articles.ordered.includes([:article, :article_price]).all.map do |oa|
|
||||||
yield [
|
yield [
|
||||||
oa.units_to_order,
|
match_params(oa, header[0]),
|
||||||
oa.article.order_number,
|
match_params(oa, header[1]),
|
||||||
oa.article.name,
|
match_params(oa, header[2]),
|
||||||
oa.article.unit,
|
match_params(oa, header[3]),
|
||||||
oa.price.unit_quantity > 1 ? oa.price.unit_quantity : nil,
|
match_params(oa, header[4]),
|
||||||
number_to_currency(oa.price.price * oa.price.unit_quantity),
|
match_params(oa, header[5]),
|
||||||
number_to_currency(oa.total_price)
|
match_params(oa, header[6])
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def match_params(object, attribute)
|
||||||
|
case attribute
|
||||||
|
when OrderArticle.human_attribute_name(:units_to_order)
|
||||||
|
object.units_to_order
|
||||||
|
when Article.human_attribute_name(:order_number)
|
||||||
|
object.article.order_number
|
||||||
|
when Article.human_attribute_name(:name)
|
||||||
|
object.article.name
|
||||||
|
when Article.human_attribute_name(:unit)
|
||||||
|
object.article.unit
|
||||||
|
when Article.human_attribute_name(:unit_quantity_short)
|
||||||
|
object.price.unit_quantity > 1 ? object.price.unit_quantity : nil
|
||||||
|
when ArticlePrice.human_attribute_name(:price)
|
||||||
|
number_to_currency(object.price.price * object.price.unit_quantity)
|
||||||
|
when OrderArticle.human_attribute_name(:total_price)
|
||||||
|
number_to_currency(object.total_price)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
59
app/lib/quantity_unit.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
class QuantityUnit
|
||||||
|
def initialize(quantity, unit)
|
||||||
|
@quantity = quantity
|
||||||
|
@unit = unit
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse(number_with_unit)
|
||||||
|
# remove whitespace
|
||||||
|
number_with_unit = number_with_unit.gsub(/\s+/, '')
|
||||||
|
# to lowercase
|
||||||
|
number_with_unit = number_with_unit.downcase
|
||||||
|
# remove numerical part
|
||||||
|
number = number_with_unit.gsub(/[^0-9.,]/, '')
|
||||||
|
# remove unit part
|
||||||
|
unit = number_with_unit.gsub(/[^a-zA-Z]/, '')
|
||||||
|
# convert comma to dot
|
||||||
|
number = number.gsub(',', '.')
|
||||||
|
# convert to float
|
||||||
|
number = number.to_f
|
||||||
|
|
||||||
|
return nil unless unit.in?(%w[g kg l ml])
|
||||||
|
|
||||||
|
QuantityUnit.new(number, unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def scale_price_to_base_unit(price)
|
||||||
|
return nil unless price.is_a?(Numeric)
|
||||||
|
|
||||||
|
factor = if @unit == 'kg' || @unit == 'l'
|
||||||
|
1
|
||||||
|
elsif @unit == 'g' || @unit == 'ml'
|
||||||
|
1000
|
||||||
|
end
|
||||||
|
|
||||||
|
scaled_price = price / @quantity * factor
|
||||||
|
scaled_price.round(2)
|
||||||
|
|
||||||
|
base_unit = if @unit == 'kg' || @unit == 'g'
|
||||||
|
'kg'
|
||||||
|
elsif @unit == 'l' || @unit == 'ml'
|
||||||
|
'L'
|
||||||
|
end
|
||||||
|
|
||||||
|
[scaled_price, base_unit]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{@quantity} #{@unit}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def quantity
|
||||||
|
@quantity
|
||||||
|
end
|
||||||
|
|
||||||
|
def unit
|
||||||
|
@unit
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,6 +20,7 @@ class RenderCsv
|
||||||
end
|
end
|
||||||
data { |d| csv << d }
|
data { |d| csv << d }
|
||||||
end
|
end
|
||||||
|
ret << I18n.t('.orders.articles.prices_sum') << ";" << "#{number_to_currency(@object.sum(:gross))}/#{number_to_currency(@object.sum(:net))}" if @options[:custom_csv]
|
||||||
ret.encode(@options[:encoding], invalid: :replace, undef: :replace)
|
ret.encode(@options[:encoding], invalid: :replace, undef: :replace)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class Supplier < ApplicationRecord
|
||||||
all_order_numbers = []
|
all_order_numbers = []
|
||||||
updated_article_pairs, outlisted_articles, new_articles = [], [], []
|
updated_article_pairs, outlisted_articles, new_articles = [], [], []
|
||||||
custom_codes_path = File.join(Rails.root, "config", "custom_codes.yml")
|
custom_codes_path = File.join(Rails.root, "config", "custom_codes.yml")
|
||||||
opts = options.except(:convert_units, :outlist_absent)
|
opts = options.except(:convert_units, :outlist_absent, :update_category)
|
||||||
custom_codes_file_path = custom_codes_path if File.exist?(custom_codes_path)
|
custom_codes_file_path = custom_codes_path if File.exist?(custom_codes_path)
|
||||||
FoodsoftArticleImport.parse(file, custom_file_path: custom_codes_file_path, type: type, **opts) do |new_attrs, status, line|
|
FoodsoftArticleImport.parse(file, custom_file_path: custom_codes_file_path, type: type, **opts) do |new_attrs, status, line|
|
||||||
article = articles.undeleted.where(order_number: new_attrs[:order_number]).first
|
article = articles.undeleted.where(order_number: new_attrs[:order_number]).first
|
||||||
|
|
15
app/views/group_orders/_explanations.haml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
%h4= t '.title'
|
||||||
|
%hr
|
||||||
|
%table.table-condensed
|
||||||
|
%thead
|
||||||
|
%th= t '.package_fill_level'
|
||||||
|
%tbody
|
||||||
|
%tr{class: "missing-none"}
|
||||||
|
%td= t '.missing_none'
|
||||||
|
%tr{class: "missing-few"}
|
||||||
|
%td= t '.missing_few'
|
||||||
|
%tr{class: "missing-many"}
|
||||||
|
%td= t '.missing_many'
|
||||||
|
%hr
|
||||||
|
%b= t('.tolerance') + ':'
|
||||||
|
= t '.tolerance_explained'
|
|
@ -22,54 +22,41 @@
|
||||||
|
|
||||||
- title t('.title'), false
|
- title t('.title'), false
|
||||||
|
|
||||||
|
.alert.alert-error#balance-alert{style: ('display:none')}
|
||||||
|
=t 'group_orders.errors.balance_alert'
|
||||||
.row-fluid
|
.row-fluid
|
||||||
.well.pull-left
|
.span2
|
||||||
= close_button :alert
|
.well
|
||||||
%h2= @order.name
|
|
||||||
%dl.dl-horizontal
|
|
||||||
- unless @order.note.blank?
|
|
||||||
%dt= heading_helper Order, :note
|
|
||||||
%dd= @order.note
|
|
||||||
%dt= heading_helper Order, :created_by
|
|
||||||
%dd= show_user_link(@order.created_by)
|
|
||||||
%dt= heading_helper Order, :ends
|
|
||||||
%dd= format_time(@order.ends)
|
|
||||||
%dt= heading_helper Order, :pickup
|
|
||||||
%dd= format_date(@order.pickup)
|
|
||||||
- unless @order.stockit? or @order.supplier.min_order_quantity.blank?
|
|
||||||
%dt= heading_helper Supplier, :min_order_quantity, short: true
|
|
||||||
%dd= @order.supplier.min_order_quantity
|
|
||||||
%dt= t '.sum_amount'
|
|
||||||
%dd= number_to_currency @order.sum
|
|
||||||
- unless @group_order.new_record?
|
|
||||||
%dt= heading_helper GroupOrder, :updated_by
|
|
||||||
%dd
|
|
||||||
= show_user(@group_order.updated_by)
|
|
||||||
(#{format_time(@group_order.updated_on)})
|
|
||||||
%dt= heading_helper Ordergroup, :account_balance
|
|
||||||
%dd= number_to_currency(@ordering_data[:account_balance])
|
|
||||||
- unless FoodsoftConfig[:charge_members_manually]
|
|
||||||
%dt= heading_helper Ordergroup, :available_funds
|
|
||||||
%dd= number_to_currency(@ordering_data[:available_funds])
|
|
||||||
|
|
||||||
.well.pull-right
|
|
||||||
= close_button :alert
|
|
||||||
= render 'switch_order', current_order: @order
|
= render 'switch_order', current_order: @order
|
||||||
|
.well
|
||||||
.row-fluid
|
= render 'explanations'
|
||||||
.well.clear
|
.well.span9
|
||||||
.form-search
|
%h2.span9= t '.sub_title', order_name: @order.name
|
||||||
|
.span3
|
||||||
|
%table.table-condensed
|
||||||
|
-if @order.ends
|
||||||
|
%tr
|
||||||
|
%td= heading_helper(Order, :ends) + ': '
|
||||||
|
%td= format_time(@order.ends)
|
||||||
|
- unless @order.stockit? or @order.supplier.min_order_quantity.blank?
|
||||||
|
%tr
|
||||||
|
%td= heading_helper(Supplier, :min_order_quantity)
|
||||||
|
%td= number_to_currency(@order.supplier.min_order_quantity)
|
||||||
|
%tr
|
||||||
|
%td= t('group_orders.form.sum_amount') + ':'
|
||||||
|
%td= number_to_currency(@order.sum)
|
||||||
|
%hr
|
||||||
|
.form-search.pull-right
|
||||||
.input-append
|
.input-append
|
||||||
= text_field_tag :article, params[:article], placeholder: t('.search_article'), class: 'search-query delayed-search resettable'
|
= text_field_tag :article, params[:article], placeholder: t('.search_article'), class: 'search-query delayed-search resettable'
|
||||||
%button.add-on.btn.reset-search{:type => :button, :title => t('.reset_article_search')}
|
%button.add-on.btn.reset-search{:type => :button, :title => t('.reset_article_search')}
|
||||||
%i.icon.icon-remove
|
%i.icon.icon-remove
|
||||||
|
|
||||||
= form_for @group_order do |f|
|
= form_for @group_order do |f|
|
||||||
= f.hidden_field :lock_version
|
= f.hidden_field :lock_version
|
||||||
= f.hidden_field :order_id
|
= f.hidden_field :order_id
|
||||||
= f.hidden_field :updated_by_user_id
|
= f.hidden_field :updated_by_user_id
|
||||||
= f.hidden_field :ordergroup_id
|
= f.hidden_field :ordergroup_id
|
||||||
%table.table.table-hover
|
%table.table
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th= heading_helper Article, :name
|
%th= heading_helper Article, :name
|
||||||
|
@ -77,6 +64,7 @@
|
||||||
%th{style: 'width:120px'}= heading_helper StockArticle, :supplier
|
%th{style: 'width:120px'}= heading_helper StockArticle, :supplier
|
||||||
%th{style: "width:13px;"}
|
%th{style: "width:13px;"}
|
||||||
%th{style: "width:4.5em;"}= t '.price'
|
%th{style: "width:4.5em;"}= t '.price'
|
||||||
|
%th{style: "width:4.5em;"}= t '.price_per_base_unit'
|
||||||
%th{style: "width:4.5em;"}= heading_helper Article, :unit
|
%th{style: "width:4.5em;"}= heading_helper Article, :unit
|
||||||
- unless @order.stockit?
|
- unless @order.stockit?
|
||||||
%th{style: "width:70px;"}= heading_helper OrderArticle, :missing_units, short: true
|
%th{style: "width:70px;"}= heading_helper OrderArticle, :missing_units, short: true
|
||||||
|
@ -94,12 +82,13 @@
|
||||||
%i.icon-tag
|
%i.icon-tag
|
||||||
%td{colspan: "9"}
|
%td{colspan: "9"}
|
||||||
- order_articles.each do |order_article|
|
- order_articles.each do |order_article|
|
||||||
%tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top"}
|
%tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top", tabindex: "0"}
|
||||||
%td.name= order_article.article.name
|
%td.name= order_article.article.name
|
||||||
- if @order.stockit?
|
- if @order.stockit?
|
||||||
%td= truncate order_article.article.supplier.name, length: 15
|
%td= truncate order_article.article.supplier.name, length: 15
|
||||||
%td= h order_article.article.origin
|
%td= h order_article.article.origin
|
||||||
%td= number_to_currency(@ordering_data[:order_articles][order_article.id][:price])
|
%td= number_to_currency(@ordering_data[:order_articles][order_article.id][:price])
|
||||||
|
%td= price_per_base_unit(article: order_article.article, price: @ordering_data[:order_articles][order_article.id][:price])
|
||||||
%td= order_article.article.unit
|
%td= order_article.article.unit
|
||||||
%td
|
%td
|
||||||
- if @order.stockit?
|
- if @order.stockit?
|
||||||
|
@ -108,15 +97,16 @@
|
||||||
%span{id: "missing_units_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:missing_units]
|
%span{id: "missing_units_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:missing_units]
|
||||||
|
|
||||||
%td.quantity
|
%td.quantity
|
||||||
|
.outer{style: "diyplay: inline-block; float: left; width: 50px;"}
|
||||||
%input{id: "q_#{order_article.id}", name: "group_order[group_order_articles_attributes][#{order_article.id}][quantity]", type: "hidden", value: @ordering_data[:order_articles][order_article.id][:quantity], 'data-min' => (@ordering_data[:order_articles][order_article.id][:quantity] if @order.boxfill?), 'data-max' => (@ordering_data[:order_articles][order_article.id][:quantity]+@ordering_data[:order_articles][order_article.id][:missing_units] if @order.boxfill?)}/
|
%input{id: "q_#{order_article.id}", name: "group_order[group_order_articles_attributes][#{order_article.id}][quantity]", type: "hidden", value: @ordering_data[:order_articles][order_article.id][:quantity], 'data-min' => (@ordering_data[:order_articles][order_article.id][:quantity] if @order.boxfill?), 'data-max' => (@ordering_data[:order_articles][order_article.id][:quantity]+@ordering_data[:order_articles][order_article.id][:missing_units] if @order.boxfill?)}/
|
||||||
%span.used{id: "q_used_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:used_quantity]
|
%span.used{id: "q_used_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:used_quantity]
|
||||||
+
|
+
|
||||||
%span.unused{id: "q_unused_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:quantity] - @ordering_data[:order_articles][order_article.id][:used_quantity]
|
%span.unused{id: "q_unused_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:quantity] - @ordering_data[:order_articles][order_article.id][:used_quantity]
|
||||||
.btn-group
|
.btn-group
|
||||||
%a.btn.btn-ordering{'data-increase_quantity' => order_article.id}
|
|
||||||
%i.icon-plus
|
|
||||||
%a.btn.btn-ordering{'data-decrease_quantity' => order_article.id}
|
%a.btn.btn-ordering{'data-decrease_quantity' => order_article.id}
|
||||||
%i.icon-minus
|
%i.icon-minus
|
||||||
|
%a.btn.btn-ordering{'data-increase_quantity' => order_article.id}
|
||||||
|
%i.icon-plus
|
||||||
|
|
||||||
%td.tolerance{style: ('display:none' if @order.stockit?)}
|
%td.tolerance{style: ('display:none' if @order.stockit?)}
|
||||||
%input{id: "t_#{order_article.id}", name: "group_order[group_order_articles_attributes][#{order_article.id}][tolerance]", type: "hidden", value: @ordering_data[:order_articles][order_article.id][:tolerance], 'data-min' => (@ordering_data[:order_articles][order_article.id][:tolerance] if @order.boxfill?)}/
|
%input{id: "t_#{order_article.id}", name: "group_order[group_order_articles_attributes][#{order_article.id}][tolerance]", type: "hidden", value: @ordering_data[:order_articles][order_article.id][:tolerance], 'data-min' => (@ordering_data[:order_articles][order_article.id][:tolerance] if @order.boxfill?)}/
|
||||||
|
@ -125,10 +115,10 @@
|
||||||
+
|
+
|
||||||
%span.unused{id: "t_unused_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:tolerance] - @ordering_data[:order_articles][order_article.id][:used_tolerance]
|
%span.unused{id: "t_unused_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:tolerance] - @ordering_data[:order_articles][order_article.id][:used_tolerance]
|
||||||
.btn-group
|
.btn-group
|
||||||
%a.btn.btn-ordering{'data-increase_tolerance' => order_article.id}
|
|
||||||
%i.icon-plus
|
|
||||||
%a.btn.btn-ordering{'data-decrease_tolerance' => order_article.id}
|
%a.btn.btn-ordering{'data-decrease_tolerance' => order_article.id}
|
||||||
%i.icon-minus
|
%i.icon-minus
|
||||||
|
%a.btn.btn-ordering{'data-increase_tolerance' => order_article.id}
|
||||||
|
%i.icon-plus
|
||||||
|
|
||||||
%td{id: "td_price_#{order_article.id}", style: "text-align:right; padding-right:10px; width:4em"}
|
%td{id: "td_price_#{order_article.id}", style: "text-align:right; padding-right:10px; width:4em"}
|
||||||
%span{id: "price_#{order_article.id}_display"}= number_to_currency(@ordering_data[:order_articles][order_article.id][:total_price])
|
%span{id: "price_#{order_article.id}_display"}= number_to_currency(@ordering_data[:order_articles][order_article.id][:total_price])
|
||||||
|
@ -154,25 +144,7 @@
|
||||||
#order-footer
|
#order-footer
|
||||||
#info-box
|
#info-box
|
||||||
#total-sum
|
#total-sum
|
||||||
%table
|
= render 'total_sum'
|
||||||
%tr
|
|
||||||
%td= t('.total_sum_amount') + ':'
|
|
||||||
%td.currency
|
|
||||||
%span#total_price= number_to_currency(@group_order.price)
|
|
||||||
%tr
|
|
||||||
- if FoodsoftConfig[:charge_members_manually]
|
|
||||||
- old_balance = @ordering_data[:account_balance]
|
|
||||||
%td= heading_helper(Ordergroup, :account_balance) + ':'
|
|
||||||
%td.currency= number_to_currency(@ordering_data[:account_balance])
|
|
||||||
- else
|
|
||||||
- old_balance = @ordering_data[:available_funds]
|
|
||||||
%td= heading_helper(Ordergroup, :available_funds) + ':'
|
|
||||||
%td.currency= number_to_currency(@ordering_data[:available_funds])
|
|
||||||
%tr
|
|
||||||
%td= t('.new_funds') + ':'
|
|
||||||
%td.currency
|
|
||||||
%strong
|
|
||||||
%span#new_balance= number_to_currency(old_balance - @group_order.price)
|
|
||||||
#order-button
|
#order-button
|
||||||
= submit_tag( t('.action_save'), id: 'submit_button', class: 'btn btn-primary' )
|
= submit_tag( t('.action_save'), id: 'submit_button', class: 'btn btn-primary' )
|
||||||
#{link_to t('ui.or_cancel'), group_orders_path}
|
#{link_to t('ui.or_cancel'), group_orders_path}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
- orders = Order.open.started.reject{ |order| order == current_order }
|
- orders = Order.open.started
|
||||||
- unless orders.empty?
|
- unless orders.empty?
|
||||||
%h2= t '.title'
|
%ul.nav.nav-pills.nav-stacked
|
||||||
%ul.unstyled
|
.nav-header= t '.title'
|
||||||
|
%li= link_to t('ui.overview'), :group_orders
|
||||||
- orders.each do |order|
|
- orders.each do |order|
|
||||||
%li
|
.btn-small.pull-right
|
||||||
= link_to_ordering(order, 'data-confirm_switch_order' => true)
|
=link_to_ordering(order, style: (order == current_order ? 'color: white' : '' ), 'data-confirm_switch_order' => true){ t 'ui.edit' }
|
||||||
- if order.ends
|
%li( class="#{ order == current_order ? 'active' : ''}")
|
||||||
= t '.remaining', remaining: time_ago_in_words(order.ends)
|
=link_to_ordering(order, show: true, 'data-confirm_switch_order' => true)
|
19
app/views/group_orders/_total_sum.haml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
%table
|
||||||
|
%tr
|
||||||
|
%td= t('group_orders.form.total_sum_amount') + ':'
|
||||||
|
%td.currency
|
||||||
|
%span#total_price= number_to_currency(@group_order.price)
|
||||||
|
%tr
|
||||||
|
- if FoodsoftConfig[:charge_members_manually]
|
||||||
|
- old_balance = @ordering_data[:account_balance]
|
||||||
|
%td= heading_helper(Ordergroup, :account_balance) + ':'
|
||||||
|
%td.currency= number_to_currency(@ordering_data[:account_balance])
|
||||||
|
- else
|
||||||
|
- old_balance = @ordering_data[:available_funds]
|
||||||
|
%td= heading_helper(Ordergroup, :available_funds) + ':'
|
||||||
|
%td.currency= number_to_currency(@ordering_data[:available_funds])
|
||||||
|
%tr
|
||||||
|
%td= t('group_orders.form.new_funds') + ':'
|
||||||
|
%td.currency
|
||||||
|
%strong
|
||||||
|
%span#new_balance= number_to_currency(old_balance - @group_order.price)
|
|
@ -18,10 +18,13 @@
|
||||||
%th= heading_helper Ordergroup, :available_funds
|
%th= heading_helper Ordergroup, :available_funds
|
||||||
%th.numeric= number_to_currency(@ordergroup.get_available_funds)
|
%th.numeric= number_to_currency(@ordergroup.get_available_funds)
|
||||||
|
|
||||||
|
.row-fluid
|
||||||
|
.span9
|
||||||
= render :partial => "shared/open_orders", :locals => {:ordergroup => @ordergroup}
|
= render :partial => "shared/open_orders", :locals => {:ordergroup => @ordergroup}
|
||||||
|
|
||||||
// finished orders
|
// finished orders
|
||||||
- unless @finished_not_closed_orders_including_group_order.empty?
|
- unless @finished_not_closed_orders_including_group_order.empty?
|
||||||
|
.row-fluid
|
||||||
|
.span9
|
||||||
%section
|
%section
|
||||||
%h2= t '.finished_orders.title'
|
%h2= t '.finished_orders.title'
|
||||||
= render partial: 'orders', locals: {orders: @finished_not_closed_orders_including_group_order, pagination: false}
|
= render partial: 'orders', locals: {orders: @finished_not_closed_orders_including_group_order, pagination: false}
|
||||||
|
@ -32,6 +35,8 @@
|
||||||
|
|
||||||
// closed orders
|
// closed orders
|
||||||
- unless @closed_orders_including_group_order.empty?
|
- unless @closed_orders_including_group_order.empty?
|
||||||
|
.row-fluid
|
||||||
|
.span9
|
||||||
%section
|
%section
|
||||||
%h2= t '.closed_orders.title'
|
%h2= t '.closed_orders.title'
|
||||||
= render partial: 'orders', locals: {orders: @closed_orders_including_group_order, pagination: false}
|
= render partial: 'orders', locals: {orders: @closed_orders_including_group_order, pagination: false}
|
||||||
|
|
|
@ -7,44 +7,52 @@
|
||||||
- title t('.title', order: @order.name)
|
- title t('.title', order: @order.name)
|
||||||
|
|
||||||
.row-fluid
|
.row-fluid
|
||||||
.well.pull-left
|
|
||||||
// Order summary
|
.well.span2
|
||||||
|
= render 'switch_order', current_order: @order
|
||||||
|
.well.span9
|
||||||
|
%h2= t '.articles.title'
|
||||||
%dl.dl-horizontal
|
%dl.dl-horizontal
|
||||||
|
// Name
|
||||||
%dt= heading_helper Order, :name
|
%dt= heading_helper Order, :name
|
||||||
%dd= @order.name
|
%dd= @order.name
|
||||||
%dt= heading_helper Order, :note
|
// Order Ends
|
||||||
%dd= @order.note
|
|
||||||
%dt= heading_helper Order, :ends
|
%dt= heading_helper Order, :ends
|
||||||
%dd= format_time(@order.ends)
|
%dd= format_time(@order.ends)
|
||||||
|
// Pickup
|
||||||
|
- unless @order.pickup.blank?
|
||||||
%dt= heading_helper Order, :pickup
|
%dt= heading_helper Order, :pickup
|
||||||
%dd= format_date(@order.pickup)
|
%dd= format_date(@order.pickup)
|
||||||
%dt= heading_helper GroupOrder, :price
|
// Min Order Quantity
|
||||||
|
- unless @order.stockit? or @order.supplier.min_order_quantity.blank?
|
||||||
|
%dt= heading_helper Supplier, :min_order_quantity, short: true
|
||||||
|
%dd= @order.supplier.min_order_quantity
|
||||||
|
// Group Order Sum Amount
|
||||||
|
%dt= t 'group_orders.form.sum_amount'
|
||||||
|
%dd= number_to_currency @order.sum
|
||||||
|
// Created By
|
||||||
|
%dt= heading_helper Order, :created_by
|
||||||
|
%dd= show_user_link(@order.created_by)
|
||||||
|
// Updated By
|
||||||
|
- unless @group_order.new_record?
|
||||||
|
%dt= heading_helper GroupOrder, :updated_by
|
||||||
%dd
|
%dd
|
||||||
- if @group_order
|
= show_user(@group_order.updated_by)
|
||||||
= number_to_currency(@group_order.price)
|
(#{format_time(@group_order.updated_on)})
|
||||||
- else
|
// Closed By
|
||||||
= t '.not_ordered'
|
|
||||||
- if @group_order && @group_order.transport
|
|
||||||
%dt= heading_helper GroupOrder, :transport
|
|
||||||
%dd= number_to_currency(@group_order.transport)
|
|
||||||
%dt= heading_helper GroupOrder, :total
|
|
||||||
%dd= number_to_currency(@group_order.total)
|
|
||||||
- if @order.closed?
|
- if @order.closed?
|
||||||
%dt= heading_helper Order, :closed_by
|
%dt= heading_helper Order, :closed_by
|
||||||
%dd= show_user_link @order.updated_by
|
%dd= show_user_link @order.updated_by
|
||||||
%p= link_to t('.comment'), "#comments"
|
// Note
|
||||||
|
- unless @order.note.blank?
|
||||||
.well.pull-right
|
%dt= heading_helper Order, :note
|
||||||
= close_button :alert
|
%dd= @order.note
|
||||||
= render 'switch_order', current_order: @order
|
|
||||||
|
|
||||||
// Article box
|
// Article box
|
||||||
%section
|
%section
|
||||||
%h2= t '.articles.title'
|
|
||||||
.column_content#result
|
.column_content#result
|
||||||
- if @group_order
|
- if @group_order
|
||||||
%p.pull-right= link_to t('.articles.show_hide'), '#', 'data-toggle-this' => 'tr.ignored'
|
%p= link_to t('.articles.show_hide'), '#', 'data-toggle-this' => 'tr.ignored'
|
||||||
%p= link_to(t('.articles.edit_order'), edit_group_order_path(@group_order, order_id: @order.id), class: 'btn btn-primary') if @order.open?
|
|
||||||
%table.table.table-hover
|
%table.table.table-hover
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
|
@ -97,15 +105,15 @@
|
||||||
%th= number_to_currency(@group_order.total)
|
%th= number_to_currency(@group_order.total)
|
||||||
%br/
|
%br/
|
||||||
= link_to_top
|
= link_to_top
|
||||||
|
%p.pull-right= link_to(t('.articles.edit_order'), edit_group_order_path(@group_order, order_id: @order.id), class: 'btn btn-primary') if @order.open?
|
||||||
- else
|
- else
|
||||||
- if @order.open?
|
- if @order.open?
|
||||||
= t '.articles.not_ordered_msg'
|
= t '.articles.not_ordered_msg'
|
||||||
= link_to t('.articles.order_now'), action: "order", id: @order
|
= link_to t('.articles.order_now'), action: "order", id: @order
|
||||||
- else
|
- else
|
||||||
= t '.articles.order_closed_msg'
|
= t '.articles.order_closed_msg'
|
||||||
|
|
||||||
// Comments box
|
// Comments box
|
||||||
%section
|
%hr
|
||||||
%h2= t '.comments.title'
|
%h2= t '.comments.title'
|
||||||
#comments
|
#comments
|
||||||
= render 'shared/comments', comments: @order.comments
|
= render 'shared/comments', comments: @order.comments
|
||||||
|
|
15
app/views/orders/_custom_csv_form.html.haml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
= simple_form_for :custom_csv,format: :csv, :url => order_path(@order, view: @view, format: :csv), method: :get do |f|
|
||||||
|
.modal-header
|
||||||
|
= close_button :modal
|
||||||
|
.h3=I18n.t('.orders.custom_csv.description')
|
||||||
|
.modal-body
|
||||||
|
= f.input :first, as: :select, collection: custom_csv_collection, label: "1. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
= f.input :second, as: :select, collection: custom_csv_collection, required: false, label: "2. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
= f.input :third, as: :select, collection: custom_csv_collection, required: false, label: "3. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
= f.input :fourth, as: :select, collection: custom_csv_collection, required: false, label: "4. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
= f.input :fifth, as: :select, collection: custom_csv_collection, required: false, label: "5. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
= f.input :sixth, as: :select, collection: custom_csv_collection, required: false, label: "6. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
= f.input :seventh, as: :select, collection: custom_csv_collection, required: false, label: "7. " + I18n.t('.orders.custom_csv.column')
|
||||||
|
.modal-footer
|
||||||
|
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
||||||
|
= f.submit class: 'btn btn-primary'
|
3
app/views/orders/custom_csv.js.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
$('#modalContainer').html('#{j(render("custom_csv_form"))}');
|
||||||
|
$('#modalContainer').modal();
|
||||||
|
$('#modalContainer').submit(function() {$('#modalContainer').modal('hide');});
|
|
@ -9,6 +9,7 @@
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th= heading_helper Order, :name
|
%th= heading_helper Order, :name
|
||||||
|
%th
|
||||||
%th= heading_helper Order, :pickup
|
%th= heading_helper Order, :pickup
|
||||||
%th= heading_helper Order, :ends
|
%th= heading_helper Order, :ends
|
||||||
%th= t '.who_ordered'
|
%th= t '.who_ordered'
|
||||||
|
@ -17,21 +18,23 @@
|
||||||
- total = 0
|
- total = 0
|
||||||
- orders.each do |order|
|
- orders.each do |order|
|
||||||
%tr
|
%tr
|
||||||
%td= link_to_ordering(order)
|
%td
|
||||||
|
= link_to_ordering(order, show: true)
|
||||||
|
%td
|
||||||
|
.btn-small= link_to_ordering(order){ t 'ui.edit' }
|
||||||
%td= format_date(order.pickup) unless order.pickup.nil?
|
%td= format_date(order.pickup) unless order.pickup.nil?
|
||||||
%td= format_time(order.ends) unless order.ends.nil?
|
%td= format_time(order.ends) unless order.ends.nil?
|
||||||
- if group_order = order.group_order(ordergroup)
|
- if group_order = order.group_order(ordergroup)
|
||||||
- total += group_order.price
|
- total += group_order.price
|
||||||
%td= "#{show_user group_order.updated_by} (#{format_time(group_order.updated_on)})"
|
%td= "#{show_user group_order.updated_by} (#{format_time(group_order.updated_on)})"
|
||||||
%td.numeric
|
%td.numeric
|
||||||
= link_to_ordering(order, show: true) do
|
|
||||||
= number_to_currency(group_order.price)
|
= number_to_currency(group_order.price)
|
||||||
- else
|
- else
|
||||||
%td{:colspan => 2}
|
%td{:colspan => 2}
|
||||||
- if total > 0
|
- if total > 0
|
||||||
%tfooter
|
%tfooter
|
||||||
%tr
|
%tr
|
||||||
%th(colspan="3")
|
%th(colspan="4")
|
||||||
%th= t('.total_sum') + ':'
|
%th= t('.total_sum') + ':'
|
||||||
%th.numeric= number_to_currency(total)
|
%th.numeric= number_to_currency(total)
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -10,3 +10,4 @@
|
||||||
- unless order.stockit?
|
- unless order.stockit?
|
||||||
%li= link_to t('.fax_txt'), order_path(order, format: :txt), {title: t('.download_file')}
|
%li= link_to t('.fax_txt'), order_path(order, format: :txt), {title: t('.download_file')}
|
||||||
%li= link_to t('.fax_csv'), order_path(order, format: :csv), {title: t('.download_file')}
|
%li= link_to t('.fax_csv'), order_path(order, format: :csv), {title: t('.download_file')}
|
||||||
|
%li= link_to t('.custom_csv'), custom_csv_order_path(order), remote: true
|
||||||
|
|
|
@ -1046,17 +1046,33 @@ de:
|
||||||
error_stale: In der Zwischenzeit hat jemand anderes auch bestellt, daher konnte die Bestellung nicht aktualisiert werden.
|
error_stale: In der Zwischenzeit hat jemand anderes auch bestellt, daher konnte die Bestellung nicht aktualisiert werden.
|
||||||
notice: Die Bestellung wurde gespeichert.
|
notice: Die Bestellung wurde gespeichert.
|
||||||
errors:
|
errors:
|
||||||
|
balance_alert: Kontostand im Minus
|
||||||
closed: Diese Bestellung ist bereits abgeschlossen.
|
closed: Diese Bestellung ist bereits abgeschlossen.
|
||||||
no_member: Du bist kein Mitglieder einer Bestellgruppe.
|
no_member: Du bist kein Mitglieder einer Bestellgruppe.
|
||||||
notfound: Fehlerhafte URL, das ist nicht Deine Bestellung.
|
notfound: Fehlerhafte URL, das ist nicht Deine Bestellung.
|
||||||
|
explanations:
|
||||||
|
package_fill_level: |
|
||||||
|
Gebindefüllstand
|
||||||
|
missing_none: |
|
||||||
|
Voll
|
||||||
|
missing_few: |
|
||||||
|
Wenig fehlt
|
||||||
|
missing_many: |
|
||||||
|
Viel fehlt
|
||||||
|
title: Erklärungen
|
||||||
|
tolerance_explained: |
|
||||||
|
Zusätzliche Menge die du bestellen würdest, damit das Gebinde voll wird.
|
||||||
|
tolerance: Toleranz
|
||||||
form:
|
form:
|
||||||
action_save: Bestellung speichern
|
action_save: Bestellung speichern
|
||||||
new_funds: Neuer Kontostand
|
new_funds: Neuer Kontostand
|
||||||
price: Preis
|
price: Preis
|
||||||
|
price_per_base_unit: Grundpreis
|
||||||
reset_article_search: Suche zurücksetzen
|
reset_article_search: Suche zurücksetzen
|
||||||
search_article: Artikel suchen...
|
search_article: Artikel suchen...
|
||||||
sum_amount: Gesamtbestellmenge bisher
|
sum_amount: Gesamtbestellmenge bisher
|
||||||
title: Bestellen
|
title: Bestellen
|
||||||
|
sub_title: Bestellung für %{order_name} aufgeben
|
||||||
total_sum_amount: Gesamtbetrag
|
total_sum_amount: Gesamtbetrag
|
||||||
total_tolerance: Gesamt-Toleranz
|
total_tolerance: Gesamt-Toleranz
|
||||||
units: Gebinde
|
units: Gebinde
|
||||||
|
@ -1100,7 +1116,6 @@ de:
|
||||||
sum: Summe
|
sum: Summe
|
||||||
title: Dein Bestellergebnis für %{order}
|
title: Dein Bestellergebnis für %{order}
|
||||||
switch_order:
|
switch_order:
|
||||||
remaining: "noch %{remaining}"
|
|
||||||
title: Laufende Bestellungen
|
title: Laufende Bestellungen
|
||||||
update:
|
update:
|
||||||
error_general: Die Bestellung konnte nicht aktualisiert werden, da ein Fehler auftrat.
|
error_general: Die Bestellung konnte nicht aktualisiert werden, da ein Fehler auftrat.
|
||||||
|
@ -1465,6 +1480,9 @@ de:
|
||||||
units_ordered: Bestellte Einheiten
|
units_ordered: Bestellte Einheiten
|
||||||
create:
|
create:
|
||||||
notice: Die Bestellung wurde erstellt.
|
notice: Die Bestellung wurde erstellt.
|
||||||
|
custom_csv:
|
||||||
|
description: Wähle die Attribute und deren Reihenfolge für die zu erzeugende CSV Datei
|
||||||
|
column: Spalte
|
||||||
edit:
|
edit:
|
||||||
title: 'Bestellung bearbeiten: %{name}'
|
title: 'Bestellung bearbeiten: %{name}'
|
||||||
edit_amount:
|
edit_amount:
|
||||||
|
|
|
@ -1048,17 +1048,33 @@ en:
|
||||||
error_stale: Someone else has ordered in the meantime, couldn't update the order.
|
error_stale: Someone else has ordered in the meantime, couldn't update the order.
|
||||||
notice: The order was saved.
|
notice: The order was saved.
|
||||||
errors:
|
errors:
|
||||||
|
balance_alert: Negative account balance
|
||||||
closed: This order is already closed.
|
closed: This order is already closed.
|
||||||
no_member: You are not a member of an ordergroup.
|
no_member: You are not a member of an ordergroup.
|
||||||
notfound: Incorrect URL, this is not your order.
|
notfound: Incorrect URL, this is not your order.
|
||||||
|
explanations:
|
||||||
|
title: Explanations
|
||||||
|
tolerance: Tolerance
|
||||||
|
package_fill_level: |
|
||||||
|
Package Fill Level
|
||||||
|
missing_none: |
|
||||||
|
No more missing
|
||||||
|
missing_few: |
|
||||||
|
Few missing
|
||||||
|
missing_many: |
|
||||||
|
Many missing
|
||||||
|
tolerance_explained: |
|
||||||
|
Additional amount you would buy to fill a wholesale package
|
||||||
form:
|
form:
|
||||||
action_save: Save order
|
action_save: Save order
|
||||||
new_funds: New account balance
|
new_funds: New account balance
|
||||||
price: Price
|
price: Price
|
||||||
|
price_per_base_unit: Base price
|
||||||
reset_article_search: Reset search
|
reset_article_search: Reset search
|
||||||
search_article: Search for articles...
|
search_article: Search for articles...
|
||||||
sum_amount: Current amount
|
sum_amount: Current amount
|
||||||
title: Orders
|
title: Orders
|
||||||
|
sub_title: Place order for %{order_name}
|
||||||
total_sum_amount: Total amount
|
total_sum_amount: Total amount
|
||||||
total_tolerance: Total tolerance
|
total_tolerance: Total tolerance
|
||||||
units: Units
|
units: Units
|
||||||
|
@ -1102,7 +1118,6 @@ en:
|
||||||
sum: Sum
|
sum: Sum
|
||||||
title: Your order result for %{order}
|
title: Your order result for %{order}
|
||||||
switch_order:
|
switch_order:
|
||||||
remaining: "%{remaining} remaining"
|
|
||||||
title: Current orders
|
title: Current orders
|
||||||
update:
|
update:
|
||||||
error_general: The order couldn’t be updated due to a bug.
|
error_general: The order couldn’t be updated due to a bug.
|
||||||
|
@ -1475,6 +1490,9 @@ en:
|
||||||
units_ordered: Units ordered
|
units_ordered: Units ordered
|
||||||
create:
|
create:
|
||||||
notice: The order was created.
|
notice: The order was created.
|
||||||
|
custom_csv:
|
||||||
|
description: Please choose the order as well as the attributes for the csv file
|
||||||
|
column: column
|
||||||
edit:
|
edit:
|
||||||
title: 'Edit order: %{name}'
|
title: 'Edit order: %{name}'
|
||||||
edit_amount:
|
edit_amount:
|
||||||
|
@ -1628,6 +1646,7 @@ en:
|
||||||
who_ordered: Who ordered?
|
who_ordered: Who ordered?
|
||||||
order_download_button:
|
order_download_button:
|
||||||
article_pdf: Article PDF
|
article_pdf: Article PDF
|
||||||
|
custom_csv: Custom CSV
|
||||||
download_file: Download file
|
download_file: Download file
|
||||||
fax_csv: Fax CSV
|
fax_csv: Fax CSV
|
||||||
fax_pdf: Fax PDF
|
fax_pdf: Fax PDF
|
||||||
|
|
|
@ -930,6 +930,7 @@ es:
|
||||||
action_save: Guardar pedido
|
action_save: Guardar pedido
|
||||||
new_funds: Nuevo balance de cuenta
|
new_funds: Nuevo balance de cuenta
|
||||||
price: Precio
|
price: Precio
|
||||||
|
price_per_base_unit: Precio de base
|
||||||
reset_article_search: Reinicia la búsqueda
|
reset_article_search: Reinicia la búsqueda
|
||||||
search_article: Busca artículos...
|
search_article: Busca artículos...
|
||||||
sum_amount: Cantidad actual
|
sum_amount: Cantidad actual
|
||||||
|
@ -1261,6 +1262,9 @@ es:
|
||||||
units_ordered: Unidades pedidas
|
units_ordered: Unidades pedidas
|
||||||
create:
|
create:
|
||||||
notice: Se ha creado el pedido
|
notice: Se ha creado el pedido
|
||||||
|
custom_csv:
|
||||||
|
description: Por favor elija el orden de los atributos así como los atributos para el archivo csv
|
||||||
|
column: columna
|
||||||
edit:
|
edit:
|
||||||
title: 'Edita pedido: %{name}'
|
title: 'Edita pedido: %{name}'
|
||||||
edit_amount:
|
edit_amount:
|
||||||
|
|
|
@ -678,6 +678,7 @@ fr:
|
||||||
action_save: Enregistrer ta commande
|
action_save: Enregistrer ta commande
|
||||||
new_funds: Nouveau solde
|
new_funds: Nouveau solde
|
||||||
price: Prix
|
price: Prix
|
||||||
|
price_per_base_unit: Prix de base
|
||||||
reset_article_search: Réinitialiser la recherche
|
reset_article_search: Réinitialiser la recherche
|
||||||
search_article: Rechercher des produits...
|
search_article: Rechercher des produits...
|
||||||
sum_amount: Quantité déjà commandée
|
sum_amount: Quantité déjà commandée
|
||||||
|
@ -1011,6 +1012,9 @@ fr:
|
||||||
units_ordered: Unités commandées
|
units_ordered: Unités commandées
|
||||||
create:
|
create:
|
||||||
notice: La commande a bien été définie.
|
notice: La commande a bien été définie.
|
||||||
|
custom_csv:
|
||||||
|
description: Veuillez choisir l'ordre des attributs ainsi que les attributs pour le fichier csv
|
||||||
|
column: colonne
|
||||||
edit:
|
edit:
|
||||||
title: 'Modifier la commande: %{name}'
|
title: 'Modifier la commande: %{name}'
|
||||||
edit_amount:
|
edit_amount:
|
||||||
|
|
|
@ -1018,6 +1018,7 @@ nl:
|
||||||
error_stale: In de tussentijd heeft iemand anders ook bestelt, daarom kon de bestelling niet bijgewerkt worden.
|
error_stale: In de tussentijd heeft iemand anders ook bestelt, daarom kon de bestelling niet bijgewerkt worden.
|
||||||
notice: Bestelling opgeslagen.
|
notice: Bestelling opgeslagen.
|
||||||
errors:
|
errors:
|
||||||
|
balance_alert: Accountsaldo in het rood
|
||||||
closed: Deze bestelling is al gesloten.
|
closed: Deze bestelling is al gesloten.
|
||||||
no_member: Je bent geen lid van dit huishouden.
|
no_member: Je bent geen lid van dit huishouden.
|
||||||
notfound: Foute URL, dit is niet jouw bestelling.
|
notfound: Foute URL, dit is niet jouw bestelling.
|
||||||
|
@ -1025,10 +1026,12 @@ nl:
|
||||||
action_save: Bestelling opslaan
|
action_save: Bestelling opslaan
|
||||||
new_funds: Nieuw tegoed
|
new_funds: Nieuw tegoed
|
||||||
price: Prijs
|
price: Prijs
|
||||||
|
price_per_base_unit: Basisprjis
|
||||||
reset_article_search: Alles tonen
|
reset_article_search: Alles tonen
|
||||||
search_article: Artikelen zoeken...
|
search_article: Artikelen zoeken...
|
||||||
sum_amount: Huidig totaalbedrag
|
sum_amount: Huidig totaalbedrag
|
||||||
title: Bestellen
|
title: Bestellen
|
||||||
|
sub_title: Plaats bestelling voor %{order_name}
|
||||||
total_sum_amount: Totalbedrag
|
total_sum_amount: Totalbedrag
|
||||||
total_tolerance: Totale tolerantie
|
total_tolerance: Totale tolerantie
|
||||||
units: Eenheden
|
units: Eenheden
|
||||||
|
@ -1072,7 +1075,6 @@ nl:
|
||||||
sum: Som
|
sum: Som
|
||||||
title: Jouw bestelling voor %{order}
|
title: Jouw bestelling voor %{order}
|
||||||
switch_order:
|
switch_order:
|
||||||
remaining: "nog %{remaining}"
|
|
||||||
title: Lopende bestellingen
|
title: Lopende bestellingen
|
||||||
update:
|
update:
|
||||||
error_general: Er is een probleem opgetreden, de bestelling kon niet bijgewerkt worden.
|
error_general: Er is een probleem opgetreden, de bestelling kon niet bijgewerkt worden.
|
||||||
|
@ -1440,6 +1442,9 @@ nl:
|
||||||
units_ordered: Bestelde eenheden
|
units_ordered: Bestelde eenheden
|
||||||
create:
|
create:
|
||||||
notice: De bestelling is aangemaakt.
|
notice: De bestelling is aangemaakt.
|
||||||
|
custom_csv:
|
||||||
|
description: Kies de volgorde van de attributen en de attributen voor het csv-bestand
|
||||||
|
column: kolom
|
||||||
edit:
|
edit:
|
||||||
title: 'Bestelling aanpassen: %{name}'
|
title: 'Bestelling aanpassen: %{name}'
|
||||||
edit_amount:
|
edit_amount:
|
||||||
|
|
|
@ -47,6 +47,7 @@ Rails.application.routes.draw do
|
||||||
get :receive
|
get :receive
|
||||||
post :receive
|
post :receive
|
||||||
|
|
||||||
|
get :custom_csv
|
||||||
get :receive_on_order_article_create
|
get :receive_on_order_article_create
|
||||||
get :receive_on_order_article_update
|
get :receive_on_order_article_update
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# default seed is minimal
|
# default seed is minimal
|
||||||
require Rails.root.join('db/seeds/minimal.seeds.rb')
|
require Rails.root.join('db/seeds/demo-seeds.rb')
|
||||||
|
|
||||||
# to generate new seeds, use the seed_dumper gem
|
# to generate new seeds, use the seed_dumper gem
|
||||||
|
|
|
@ -39,6 +39,9 @@ nkn_supplier = Supplier.create!(
|
||||||
)
|
)
|
||||||
|
|
||||||
chocolate_category = ArticleCategory.create!(name: "Schokolade")
|
chocolate_category = ArticleCategory.create!(name: "Schokolade")
|
||||||
|
obst_category = ArticleCategory.create!(name: "Obst, Gemüse, Sprossen, Pilze")
|
||||||
|
nudeln_category = ArticleCategory.create!(name: "Nudeln, Trockenfrüchte, Müsli")
|
||||||
|
reis_category = ArticleCategory.create!(name: "Getreide, Ölsaaten. Nußkerne")
|
||||||
|
|
||||||
Article.create!(
|
Article.create!(
|
||||||
name: "Vollmilch-Schokolade",
|
name: "Vollmilch-Schokolade",
|
||||||
|
@ -95,7 +98,7 @@ seed_order(supplier_id: chocolate_supplier.id, starts: 0.days.ago, ends: 7.days.
|
||||||
apple = Article.create!(
|
apple = Article.create!(
|
||||||
name: "Äpfel Elstar",
|
name: "Äpfel Elstar",
|
||||||
supplier_id: nkn_supplier.id,
|
supplier_id: nkn_supplier.id,
|
||||||
article_category_id: supplier_category.id,
|
article_category_id: obst_category.id,
|
||||||
manufacturer: "Obsthof Bruno Brugger",
|
manufacturer: "Obsthof Bruno Brugger",
|
||||||
origin: "D", price: 3.49, tax: 7.0,
|
origin: "D", price: 3.49, tax: 7.0,
|
||||||
unit: "1kg", unit_quantity: 10,
|
unit: "1kg", unit_quantity: 10,
|
||||||
|
@ -105,7 +108,7 @@ apple = Article.create!(
|
||||||
brokkoli = Article.create!(
|
brokkoli = Article.create!(
|
||||||
name: "Brokkoli",
|
name: "Brokkoli",
|
||||||
supplier_id: nkn_supplier.id,
|
supplier_id: nkn_supplier.id,
|
||||||
article_category_id: supplier_category.id,
|
article_category_id: obst_category.id,
|
||||||
manufacturer: "Fattoria degli Orsi",
|
manufacturer: "Fattoria degli Orsi",
|
||||||
origin: "IT", price: 2.89, tax: 7.0,
|
origin: "IT", price: 2.89, tax: 7.0,
|
||||||
unit: "400g", unit_quantity: 6,
|
unit: "400g", unit_quantity: 6,
|
||||||
|
@ -115,17 +118,17 @@ brokkoli = Article.create!(
|
||||||
tomatoes = Article.create!(
|
tomatoes = Article.create!(
|
||||||
name: "Tomaten",
|
name: "Tomaten",
|
||||||
supplier_id: nkn_supplier.id,
|
supplier_id: nkn_supplier.id,
|
||||||
article_category_id: supplier_category.id,
|
article_category_id: obst_category.id,
|
||||||
manufacturer: "Terra di Puglia",
|
manufacturer: "Terra di Puglia",
|
||||||
origin: "IT", price: 2.89, tax: 7.0,
|
origin: "IT", price: 2.89, tax: 7.0,
|
||||||
unit: "500g", unit_quantity: 20,
|
unit: "500g", unit_quantity: 20,
|
||||||
note: "pomodori italianio, demeter",
|
note: "pomodori italiani, demeter",
|
||||||
availability: true, order_number: "7")
|
availability: true, order_number: "7")
|
||||||
|
|
||||||
rice = Article.create!(
|
rice = Article.create!(
|
||||||
name: "Reis",
|
name: "Reis",
|
||||||
supplier_id: nkn_supplier.id,
|
supplier_id: nkn_supplier.id,
|
||||||
article_category_id: supplier_category.id,
|
article_category_id: reis_category.id,
|
||||||
manufacturer: "Finck",
|
manufacturer: "Finck",
|
||||||
origin: "D", price: 3.29, tax: 7.0,
|
origin: "D", price: 3.29, tax: 7.0,
|
||||||
unit: "3kg", unit_quantity: 10,
|
unit: "3kg", unit_quantity: 10,
|
||||||
|
@ -135,7 +138,7 @@ rice = Article.create!(
|
||||||
spaghetti = Article.create!(
|
spaghetti = Article.create!(
|
||||||
name: "Spaghetti",
|
name: "Spaghetti",
|
||||||
supplier_id: nkn_supplier.id,
|
supplier_id: nkn_supplier.id,
|
||||||
article_category_id: supplier_category.id,
|
article_category_id: nudeln_category.id,
|
||||||
manufacturer: "Pastificio Zanellini spa",
|
manufacturer: "Pastificio Zanellini spa",
|
||||||
origin: "D", price: 2.89, tax: 7.0,
|
origin: "D", price: 2.89, tax: 7.0,
|
||||||
unit: "500g", unit_quantity: 4,
|
unit: "500g", unit_quantity: 4,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1
|
BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1
|
||||||
5;;;;4280001958081;4280001958203;Žpfel Elstar;erntefrisch und knackig;;;obb;;D;C%;DE-™KO-001;120;0301;10;55;;1;10 x1Kg;10;1Kg;1;N;;;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;1;;
|
5;;;;4280001958081;4280001958203;Žpfel Elstar;erntefrisch und knackig;;;obb;;D;C%;DE-™KO-001;120;0301;10;55;;1;10 x1kg;10;1kg;1;N;;;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;1;;
|
||||||
6;;;;4280001958081;4280001958203;Brokkoli;aus der Erde;;;VIB;;IT;C%;DE-™KO-001;120;03;10;55;;1;4 x400g;4;400g;1;N;;;;1,41;;;;1;;;4,49;3,20;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2,5;;
|
6;;;;4280001958081;4280001958203;Brokkoli;gesund und lecker;;;fig;;IT;C%;DE-™KO-001;120;03;10;55;;1;6 x400g;6;400g;1;N;;;;1,41;;;;1;;;4,49;2,99;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2,5;;
|
||||||
7;;;;4280001958081;4280001958203;Tomaten;Datteltomaten, demeter;;;TDP;;IT;C%;DE-™KO-001;120;03;10;55;;1;20 x500g;20;500g;1;N;;;;1,41;;;;1;;;4,49;3,00;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2;;
|
7;;;;4280001958081;4280001958203;Tomaten;pomodori italiani, demeter;;;TDP;;IT;C%;DE-™KO-001;120;03;10;55;;1;20 x500g;20;500g;1;N;;;;1,41;;;;1;;;4,49;3,19;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2;;
|
||||||
8;;;;4280001958081;4280001958203;Reis;Reispfannen geeignet;;;FIN;;D;C%;DE-™KO-001;120;05;10;55;;1;12 x300g;12;300g;1;N;;;;1,41;;;;1;;;4,49;3,00;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;3,333333;;
|
8;;;;4280001958081;4280001958203;Reis;Reis im Vorratssack, demeter;;;FIN;;D;C%;DE-™KO-001;120;05;10;55;;1;12 x3k;12;3kg;1;N;;;;1,41;;;;1;;;4,49;3,49;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;0,3;;
|
||||||
9;;;;4280001958081;4280001958203;Spaghetti;Vollkorn;;;ZLN;;D;C%;DE-™KO-001;120;06;10;55;;1;4 x500g;4;500g;1;N;;;;1,41;;;;1;;;4,49;3,00;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2;;
|
9;;;;4280001958081;4280001958203;Spaghetti;100% italienisches Hartweizengrieá;;;ZLN;;D;C%;DE-™KO-001;120;06;10;55;;1;4 x500g;4;500g;1;N;;;;1,41;;;;1;;;4,49;2,99;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2;;
|
||||||
10;;;;4280001958081;4280001958203;Kartoffeln;vorwiegend festkochend;;;rsh;;D;C%;DE-™KO-001;120;0311;10;55;;1;6 x5Kg;6;5Kg;1;N;;;;1,41;;;;1;;;4,49;3,00;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;0.2;;
|
10;;;;4280001958081;4280001958203;Kartoffeln;vorwiegend festkochend;;;rsh;;D;C%;DE-™KO-001;120;0311;10;55;;1;6 x5Kg;6;5Kg;1;N;;;;1,41;;;;1;;;4,49;3,00;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;0.2;;
|
BIN
doc/foodcoop-explained.jpg
Normal file
After Width: | Height: | Size: 392 KiB |
1
doc/logo-bmbf.svg
Normal file
After Width: | Height: | Size: 11 KiB |
1
doc/logo-okfn.svg
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
doc/screenshots/balance_sum.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
doc/screenshots/bnn_upload.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
doc/screenshots/custom_csv_export.png
Normal file
After Width: | Height: | Size: 165 KiB |
BIN
doc/screenshots/message_formatting.png
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
doc/screenshots/order.png
Normal file
After Width: | Height: | Size: 149 KiB |
BIN
doc/screenshots/rswag.png
Normal file
After Width: | Height: | Size: 101 KiB |
|
@ -8,7 +8,10 @@ feature ArticlesController do
|
||||||
before { login user }
|
before { login user }
|
||||||
|
|
||||||
describe ':index', js: true do
|
describe ':index', js: true do
|
||||||
before { visit supplier_articles_path(supplier_id: supplier.id) }
|
before {
|
||||||
|
login user
|
||||||
|
visit supplier_articles_path(supplier_id: supplier.id)
|
||||||
|
}
|
||||||
|
|
||||||
it 'can visit supplier articles path' do
|
it 'can visit supplier articles path' do
|
||||||
expect(page).to have_content(supplier.name)
|
expect(page).to have_content(supplier.name)
|
||||||
|
|
22
spec/lib/quantity_unit_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
describe QuantityUnit do
|
||||||
|
it "parses a string correctly" do
|
||||||
|
qu = QuantityUnit.parse("1.5 k g"); expect([qu.quantity, qu.unit]).to eq([1.5, "kg"])
|
||||||
|
qu = QuantityUnit.parse(" 1,5 kg"); expect([qu.quantity, qu.unit]).to eq([1.5, "kg"])
|
||||||
|
qu = QuantityUnit.parse("1500 g"); expect([qu.quantity, qu.unit]).to eq([1500, "g"])
|
||||||
|
qu = QuantityUnit.parse("1.5L "); expect([qu.quantity, qu.unit]).to eq([1.5, "l"])
|
||||||
|
qu = QuantityUnit.parse("2400mL"); expect([qu.quantity, qu.unit]).to eq([2400, "ml"])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "scales prices correctly" do
|
||||||
|
qu = QuantityUnit.new(1.5, "kg")
|
||||||
|
expect(qu.scale_price_to_base_unit(12.34)).to eq([8.23, "kg"])
|
||||||
|
qu = QuantityUnit.new(1500, "g")
|
||||||
|
expect(qu.scale_price_to_base_unit(12.34)).to eq([8.23, "kg"])
|
||||||
|
qu = QuantityUnit.new(1.5, "l")
|
||||||
|
expect(qu.scale_price_to_base_unit(12.34)).to eq([8.23, "L"])
|
||||||
|
qu = QuantityUnit.new(2400, "ml")
|
||||||
|
expect(qu.scale_price_to_base_unit(12.34)).to eq([5.14, "L"])
|
||||||
|
end
|
||||||
|
end
|