Compare commits
27 commits
tantewande
...
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 | |||
|
78da4feafe |
25
.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_tantewandel"
|
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_tantewandel"
|
STACK_NAME: "foodsoft_${DRONE_BRANCH}"
|
||||||
DOMAIN: "tantewandel.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: Tantewandel
|
FOODCOOP_NAME: Einkaufskooperative Foobar
|
||||||
FOODCOOP_CITY: Mechow
|
FOODCOOP_CITY: Berlin
|
||||||
FOODCOOP_COUNTRY: Deutschland
|
FOODCOOP_COUNTRY: Deutschland
|
||||||
FOODCOOP_EMAIL: info@tantewandel.de
|
FOODCOOP_EMAIL: foodsoft@local-it.org
|
||||||
FOODCOOP_PHONE:
|
FOODCOOP_PHONE: 123456789
|
||||||
FOODCOOP_STREET:
|
FOODCOOP_STREET: Einkaufsstraße 5
|
||||||
FOODCOOP_ZIP_CODE:
|
FOODCOOP_ZIP_CODE: 12345
|
||||||
FOODCOOP_HOMEPAGE: https://tantewandel.de
|
FOODCOOP_HOMEPAGE: https://foodsoft.local-it.org
|
||||||
FOODCOOP_HELP_URL: https://tantewandel.de
|
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
|
||||||
|
@ -143,4 +143,3 @@ steps:
|
||||||
trigger:
|
trigger:
|
||||||
branch:
|
branch:
|
||||||
- demo
|
- demo
|
||||||
- tantewandel
|
|
||||||
|
|
|
@ -261,8 +261,6 @@ Lint/Void:
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
|
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 143
|
Max: 143
|
||||||
Exclude:
|
|
||||||
- 'app/documents/group_order_invoice_pdf.rb'
|
|
||||||
|
|
||||||
# Offense count: 17
|
# Offense count: 17
|
||||||
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods, inherit_mode.
|
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods, inherit_mode.
|
||||||
|
@ -404,16 +402,22 @@ RSpec/BeforeAfterAll:
|
||||||
# Configuration parameters: EnabledMethods.
|
# Configuration parameters: EnabledMethods.
|
||||||
RSpec/Capybara/FeatureMethods:
|
RSpec/Capybara/FeatureMethods:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/integration/articles_spec.rb'
|
- "spec/integration/articles_spec.rb"
|
||||||
- 'spec/integration/balancing_spec.rb'
|
- "spec/integration/balancing_spec.rb"
|
||||||
- 'spec/integration/config_spec.rb'
|
- "spec/integration/config_spec.rb"
|
||||||
- 'spec/integration/login_spec.rb'
|
- "spec/integration/home_spec.rb"
|
||||||
- 'spec/integration/order_spec.rb'
|
- "spec/integration/login_spec.rb"
|
||||||
- 'spec/integration/product_distribution_example_spec.rb'
|
- "spec/integration/order_spec.rb"
|
||||||
- 'spec/integration/receive_spec.rb'
|
- "spec/integration/product_distribution_example_spec.rb"
|
||||||
- 'spec/integration/session_spec.rb'
|
- "spec/integration/receive_spec.rb"
|
||||||
- 'spec/integration/supplier_spec.rb'
|
- "spec/integration/session_spec.rb"
|
||||||
- 'spec/integration/group_order_invoices_spec.rb'
|
- "spec/integration/supplier_spec.rb"
|
||||||
|
|
||||||
|
# Offense count: 4
|
||||||
|
RSpec/Capybara/SpecificMatcher:
|
||||||
|
Exclude:
|
||||||
|
- "spec/integration/login_spec.rb"
|
||||||
|
- "spec/integration/session_spec.rb"
|
||||||
|
|
||||||
# Offense count: 27
|
# Offense count: 27
|
||||||
# Configuration parameters: Prefixes, AllowedPatterns.
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
||||||
|
|
|
@ -53,9 +53,10 @@ RUN export DATABASE_URL=mysql2://localhost/temp?encoding=utf8 && \
|
||||||
rm -Rf /var/lib/apt/lists/* /var/cache/apt/*
|
rm -Rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||||
|
|
||||||
# Make relevant dirs and files writable for app user
|
# Make relevant dirs and files writable for app user
|
||||||
RUN mkdir -p tmp && \
|
RUN mkdir -p tmp storage && \
|
||||||
chown nobody config/app_config.yml && \
|
chown nobody config/app_config.yml && \
|
||||||
chown nobody tmp
|
chown nobody tmp && \
|
||||||
|
chown nobody storage
|
||||||
|
|
||||||
# Run app as unprivileged user
|
# Run app as unprivileged user
|
||||||
USER nobody
|
USER nobody
|
||||||
|
|
3
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'
|
||||||
|
@ -49,6 +49,7 @@ gem 'attribute_normalizer'
|
||||||
gem 'ice_cube'
|
gem 'ice_cube'
|
||||||
# At time of development 01-06-2022 mmddyyyy necessary fix for config_helper.rb form builder was not in rubygems so we pull from github, see: https://github.com/gregschmit/recurring_select/pull/152
|
# At time of development 01-06-2022 mmddyyyy necessary fix for config_helper.rb form builder was not in rubygems so we pull from github, see: https://github.com/gregschmit/recurring_select/pull/152
|
||||||
gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select'
|
gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select'
|
||||||
|
gem 'foodsoft_article_import', git: 'https://git.local-it.org/Foodsoft/foodsoft_article_import', tag: 'v1.0'
|
||||||
gem 'roo'
|
gem 'roo'
|
||||||
gem 'roo-xls'
|
gem 'roo-xls'
|
||||||
gem 'spreadsheet'
|
gem 'spreadsheet'
|
||||||
|
|
16
Gemfile.lock
|
@ -1,3 +1,11 @@
|
||||||
|
GIT
|
||||||
|
remote: https://git.local-it.org/Foodsoft/foodsoft_article_import
|
||||||
|
revision: 49a0c1ddb3bb67a357c692c63af0cda2db7c45b0
|
||||||
|
tag: v1.0
|
||||||
|
specs:
|
||||||
|
foodsoft_article_import (1.0.0)
|
||||||
|
roo (~> 2.9.0)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/gregschmit/recurring_select
|
remote: https://github.com/gregschmit/recurring_select
|
||||||
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
|
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
|
||||||
|
@ -234,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)
|
||||||
|
@ -624,6 +631,7 @@ DEPENDENCIES
|
||||||
exception_notification
|
exception_notification
|
||||||
factory_bot_rails
|
factory_bot_rails
|
||||||
faker
|
faker
|
||||||
|
foodsoft_article_import!
|
||||||
foodsoft_discourse!
|
foodsoft_discourse!
|
||||||
foodsoft_documents!
|
foodsoft_documents!
|
||||||
foodsoft_links!
|
foodsoft_links!
|
||||||
|
@ -631,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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,13 +278,14 @@ tr.order-article .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.order-article:focus{
|
|
||||||
background-color: #E4EED6;
|
|
||||||
}
|
|
||||||
tr.order-article:focus .article-info {
|
tr.order-article:focus .article-info {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr.order-article:focus {
|
||||||
|
background-color: #E9E9E9;
|
||||||
|
}
|
||||||
|
|
||||||
// ********* Articles
|
// ********* Articles
|
||||||
|
|
||||||
tr.just-updated {
|
tr.just-updated {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,11 @@ class ArticlesController < ApplicationController
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@article = Article.find(params[:id])
|
||||||
|
render :action => 'new', :layout => false
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@article = Article.new(params[:article])
|
@article = Article.new(params[:article])
|
||||||
if @article.valid? && @article.save
|
if @article.valid? && @article.save
|
||||||
|
@ -55,11 +60,6 @@ class ArticlesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@article = Article.find(params[:id])
|
|
||||||
render :action => 'new', :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Updates one Article and highlights the line if succeded
|
# Updates one Article and highlights the line if succeded
|
||||||
def update
|
def update
|
||||||
@article = Article.find(params[:id])
|
@article = Article.find(params[:id])
|
||||||
|
@ -148,10 +148,12 @@ class ArticlesController < ApplicationController
|
||||||
# Update articles from a spreadsheet
|
# Update articles from a spreadsheet
|
||||||
def parse_upload
|
def parse_upload
|
||||||
uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file')
|
uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file')
|
||||||
|
type = params[:articles]['type']
|
||||||
options = { filename: uploaded_file.original_filename }
|
options = { filename: uploaded_file.original_filename }
|
||||||
options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1')
|
options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1')
|
||||||
options[:convert_units] = (params[:articles]['convert_units'] == '1')
|
options[:convert_units] = (params[:articles]['convert_units'] == '1')
|
||||||
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, options
|
options[:update_category] = (params[:articles]['update_category'] == '1')
|
||||||
|
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, type, options
|
||||||
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
||||||
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice')
|
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice')
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
module Concerns::SendGroupOrderInvoicePdf
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def send_group_order_invoice_pdf(group_order_invoice)
|
|
||||||
invoice_data = group_order_invoice.load_data_for_invoice
|
|
||||||
invoice_data[:title] = t('documents.group_order_invoice_pdf.title', supplier: invoice_data[:supplier])
|
|
||||||
invoice_data[:no_footer] = true
|
|
||||||
pdf = GroupOrderInvoicePdf.new invoice_data
|
|
||||||
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -5,7 +5,7 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@order = Order.find(params[:order_id])
|
@order = Order.find(params[:order_id])
|
||||||
flash.now.alert = t('finance.balancing.new.alert') if @order.closed? && flash[:alert].blank?
|
flash.now.alert = t('finance.balancing.new.alert') if @order.closed?
|
||||||
@comments = @order.comments
|
@comments = @order.comments
|
||||||
|
|
||||||
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
||||||
|
@ -81,24 +81,9 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
||||||
@order.close!(@current_user, @type)
|
@order.close!(@current_user, @type)
|
||||||
note = t('finance.balancing.close.notice')
|
redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice')
|
||||||
if @order.closed?
|
|
||||||
alert = t('finance.balancing.close.alert')
|
|
||||||
if FoodsoftConfig[:group_order_invoices]&.[](:use_automatic_invoices)
|
|
||||||
@order.group_orders.each do |go|
|
|
||||||
alert = t('finance.balancing.close.settings_not_set')
|
|
||||||
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
|
|
||||||
if goi.save!
|
|
||||||
NotifyGroupOrderInvoiceJob.perform_later(goi)
|
|
||||||
note = t('finance.balancing.close.notice_mail')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alert ||= t('finance.balancing.close.alert')
|
|
||||||
redirect_to finance_order_index_url, notice: note
|
|
||||||
rescue => error
|
rescue => error
|
||||||
redirect_to new_finance_order_url(order_id: @order.id), notice: note, alert: alert, msg: error.message
|
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the order directly, without automaticly updating ordergroups account balances
|
# Close the order directly, without automaticly updating ordergroups account balances
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Finance::FinancialTransactionsController < ApplicationController
|
||||||
sort = "created_on DESC"
|
sort = "created_on DESC"
|
||||||
end
|
end
|
||||||
|
|
||||||
@q = FinancialTransaction.search(params[:q])
|
@q = FinancialTransaction.ransack(params[:q])
|
||||||
@financial_transactions_all = @q.result(distinct: true).includes(:user).order(sort)
|
@financial_transactions_all = @q.result(distinct: true).includes(:user).order(sort)
|
||||||
@financial_transactions_all = @financial_transactions_all.visible unless params[:show_hidden]
|
@financial_transactions_all = @financial_transactions_all.visible unless params[:show_hidden]
|
||||||
@financial_transactions_all = @financial_transactions_all.where(ordergroup_id: @ordergroup.id) if @ordergroup
|
@financial_transactions_all = @financial_transactions_all.where(ordergroup_id: @ordergroup.id) if @ordergroup
|
||||||
|
|
|
@ -11,7 +11,10 @@ class Finance::OrdergroupsController < Finance::BaseController
|
||||||
@ordergroups = Ordergroup.undeleted.order(sort)
|
@ordergroups = Ordergroup.undeleted.order(sort)
|
||||||
@ordergroups = @ordergroups.include_transaction_class_sum
|
@ordergroups = @ordergroups.include_transaction_class_sum
|
||||||
@ordergroups = @ordergroups.where('groups.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
|
@ordergroups = @ordergroups.where('groups.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
|
||||||
|
|
||||||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
|
@total_balances = FinancialTransactionClass.sorted.each_with_object({}) do |c, tmp|
|
||||||
|
tmp[c.id] = c.financial_transactions.reduce(0) { | sum, t | sum + t.amount }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
class GroupOrderInvoicesController < ApplicationController
|
|
||||||
include Concerns::SendGroupOrderInvoicePdf
|
|
||||||
before_action :authenticate_finance
|
|
||||||
|
|
||||||
def show
|
|
||||||
begin
|
|
||||||
@group_order_invoice = GroupOrderInvoice.find(params[:id])
|
|
||||||
if FoodsoftConfig[:contact][:tax_number]
|
|
||||||
respond_to do |format|
|
|
||||||
format.pdf do
|
|
||||||
send_group_order_invoice_pdf @group_order_invoice if FoodsoftConfig[:contact][:tax_number]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise RecordInvalid
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::RecordInvalid => error
|
|
||||||
redirect_back fallback_location: root_path, notice: 'Something went wrong', alert: I18n.t('errors.general_msg', msg: "#{error} " + I18n.t('errors.check_tax_number'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
goi = GroupOrderInvoice.find(params[:id])
|
|
||||||
@order = goi.group_order.order
|
|
||||||
goi.destroy
|
|
||||||
respond_to do |format|
|
|
||||||
format.js
|
|
||||||
format.json { head :no_content }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_multiple
|
|
||||||
invoice_date = params[:group_order_invoice][:invoice_date]
|
|
||||||
order_id = params[:group_order_invoice][:order_id]
|
|
||||||
@order = Order.find(order_id)
|
|
||||||
gos = GroupOrder.where("order_id = ?", order_id)
|
|
||||||
gos.each do |go|
|
|
||||||
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
|
|
||||||
goi.invoice_date = invoice_date
|
|
||||||
goi.invoice_number = goi.generate_invoice_number(1)
|
|
||||||
goi.save!
|
|
||||||
end
|
|
||||||
respond_to do |format|
|
|
||||||
format.js
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
go = GroupOrder.find(params[:group_order])
|
|
||||||
@order = go.order
|
|
||||||
GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
|
|
||||||
respond_to do |format|
|
|
||||||
format.js
|
|
||||||
end
|
|
||||||
redirect_back fallback_location: root_path
|
|
||||||
rescue => error
|
|
||||||
redirect_back fallback_location: root_path, notice: 'Something went wrong', :alert => I18n.t('errors.general_msg', :msg => error)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -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]
|
||||||
|
|
|
@ -1,202 +0,0 @@
|
||||||
class GroupOrderInvoicePdf < RenderPdf
|
|
||||||
def filename
|
|
||||||
I18n.t('documents.group_order_invoice_pdf.filename', :number => @options[:invoice_number]) + '.pdf'
|
|
||||||
end
|
|
||||||
|
|
||||||
def title
|
|
||||||
I18n.t('documents.group_order_invoice_pdf.title', :supplier => @options[:supplier])
|
|
||||||
end
|
|
||||||
|
|
||||||
def body
|
|
||||||
contact = FoodsoftConfig[:contact].symbolize_keys
|
|
||||||
ordergroup = @options[:ordergroup]
|
|
||||||
|
|
||||||
# From paragraph
|
|
||||||
bounding_box [margin_box.right - 200, margin_box.top - 20], width: 200 do
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.invoicer')
|
|
||||||
move_down 7
|
|
||||||
text FoodsoftConfig[:name], size: fontsize(9), align: :left
|
|
||||||
move_down 5
|
|
||||||
text contact[:street], size: fontsize(9), align: :left
|
|
||||||
move_down 5
|
|
||||||
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :left
|
|
||||||
move_down 5
|
|
||||||
unless contact[:phone].blank?
|
|
||||||
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :left
|
|
||||||
move_down 5
|
|
||||||
end
|
|
||||||
unless contact[:email].blank?
|
|
||||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :left
|
|
||||||
end
|
|
||||||
move_down 5
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.tax_number', :number => @options[:tax_number]), size: fontsize(9), align: :left
|
|
||||||
end
|
|
||||||
|
|
||||||
# Receiving Ordergroup
|
|
||||||
bounding_box [margin_box.left, margin_box.top - 20], width: 200 do
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.invoicee')
|
|
||||||
move_down 7
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.ordergroup.name', ordergroup: ordergroup.name.to_s), size: fontsize(9)
|
|
||||||
move_down 5
|
|
||||||
if ordergroup.contact_address
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.ordergroup.contact_address', contact_address: ordergroup.contact_address.to_s), size: fontsize(9)
|
|
||||||
move_down 5
|
|
||||||
end
|
|
||||||
if ordergroup.contact_phone
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.ordergroup.contact_phone', contact_phone: ordergroup.contact_phone.to_s), size: fontsize(9)
|
|
||||||
move_down 5
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# invoice Date and nnvoice number
|
|
||||||
bounding_box [margin_box.right - 200, margin_box.top - 150], width: 200 do
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.invoice_date', invoice_date: @options[:invoice_date].strftime(I18n.t('date.formats.default'))), align: :left
|
|
||||||
move_down 5
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.invoice_number', invoice_number: @options[:invoice_number]), align: :left
|
|
||||||
end
|
|
||||||
|
|
||||||
move_down 15
|
|
||||||
|
|
||||||
# kind of the "body" of the invoice
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.payment_method', payment_method: @options[:payment_method])
|
|
||||||
move_down 15
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.table_headline')
|
|
||||||
move_down 5
|
|
||||||
|
|
||||||
#------------- Table Data -----------------------
|
|
||||||
|
|
||||||
@group_order = GroupOrder.find(@options[:group_order].id)
|
|
||||||
if FoodsoftConfig[:group_order_invoices][:vat_exempt]
|
|
||||||
body_for_vat_exempt
|
|
||||||
else
|
|
||||||
body_with_vat
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def body_for_vat_exempt
|
|
||||||
total_gross = 0
|
|
||||||
data = [I18n.t('documents.group_order_invoice_pdf.vat_exempt_rows')]
|
|
||||||
move_down 10
|
|
||||||
group_order_articles = GroupOrderArticle.where(group_order_id: @group_order.id)
|
|
||||||
group_order_articles.each do |goa|
|
|
||||||
# if no unit is received, nothing is to be charged
|
|
||||||
next if goa.result.to_i == 0
|
|
||||||
goa_total_gross = goa.result * goa.order_article.price.gross_price
|
|
||||||
data << [goa.order_article.article.name,
|
|
||||||
goa.result.to_i,
|
|
||||||
number_to_currency(goa.order_article.price.gross_price),
|
|
||||||
number_to_currency(goa.total_price)]
|
|
||||||
total_gross += goa_total_gross
|
|
||||||
end
|
|
||||||
|
|
||||||
table data, position: :left, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
|
||||||
table.header = true
|
|
||||||
table.position = :center
|
|
||||||
table.cells.border_width = 1
|
|
||||||
table.cells.border_color = '666666'
|
|
||||||
|
|
||||||
table.row(0).column(1).width = 40
|
|
||||||
table.row(0).border_bottom_width = 2
|
|
||||||
table.columns(1).align = :right
|
|
||||||
table.columns(1..6).align = :right
|
|
||||||
end
|
|
||||||
|
|
||||||
move_down 5
|
|
||||||
sum = []
|
|
||||||
sum << [nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay'), number_to_currency(total_gross)]
|
|
||||||
# table for sum
|
|
||||||
indent(200) do
|
|
||||||
table sum, position: :center, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
|
||||||
sum.length.times do |count|
|
|
||||||
table.row(count).columns(0..3).borders = []
|
|
||||||
end
|
|
||||||
table.row(sum.length - 1).columns(0..2).borders = []
|
|
||||||
table.row(sum.length - 1).border_bottom_width = 2
|
|
||||||
table.row(sum.length - 1).columns(3).borders = [:bottom]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
move_down 25
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
|
|
||||||
move_down 10
|
|
||||||
end
|
|
||||||
|
|
||||||
def body_with_vat
|
|
||||||
total_gross = 0
|
|
||||||
total_net = 0
|
|
||||||
# Articles
|
|
||||||
|
|
||||||
tax_hash_net = Hash.new(0) # for summing up article net prices grouped into vat percentage
|
|
||||||
tax_hash_gross = Hash.new(0) # same here with gross prices
|
|
||||||
|
|
||||||
marge = FoodsoftConfig[:price_markup]
|
|
||||||
|
|
||||||
# data table looks different when price_markup > 0
|
|
||||||
data = if marge == 0
|
|
||||||
[I18n.t('documents.group_order_invoice_pdf.no_price_markup_rows')]
|
|
||||||
else
|
|
||||||
[I18n.t('documents.group_order_invoice_pdf.price_markup_rows', marge: marge)]
|
|
||||||
end
|
|
||||||
goa_tax_hash = GroupOrderArticle.where(group_order_id: @group_order.id).find_each.group_by { |oat| oat.order_article.price.tax }
|
|
||||||
goa_tax_hash.each do |tax, group_order_articles|
|
|
||||||
group_order_articles.each do |goa|
|
|
||||||
# if no unit is received, nothing is to be charged
|
|
||||||
next if goa.result.to_i == 0
|
|
||||||
|
|
||||||
order_article = goa.order_article
|
|
||||||
goa_total_net = goa.result * order_article.price.price
|
|
||||||
goa_total_gross = goa.result * order_article.price.gross_price
|
|
||||||
data << [order_article.article.name,
|
|
||||||
goa.result.to_i,
|
|
||||||
number_to_currency(order_article.price.price),
|
|
||||||
number_to_currency(goa_total_net),
|
|
||||||
tax.to_s + '%',
|
|
||||||
number_to_currency(goa.total_price)]
|
|
||||||
tax_hash_net[tax.to_i] += goa_total_net
|
|
||||||
tax_hash_gross[tax.to_i] += goa_total_gross
|
|
||||||
total_net += goa_total_net
|
|
||||||
total_gross += goa_total_gross
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Two separate tables for sum and individual data
|
|
||||||
# article information + data
|
|
||||||
table data, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
|
||||||
table.header = true
|
|
||||||
table.position = :center
|
|
||||||
table.cells.border_width = 1
|
|
||||||
table.cells.border_color = '666666'
|
|
||||||
|
|
||||||
table.row(0).column(1).width = 40
|
|
||||||
table.row(0).border_bottom_width = 2
|
|
||||||
table.columns(1).align = :right
|
|
||||||
table.columns(1..6).align = :right
|
|
||||||
end
|
|
||||||
|
|
||||||
sum = []
|
|
||||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_net'), number_to_currency(total_net)]
|
|
||||||
tax_hash_net.each_key.each do |tax|
|
|
||||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.tax_included', tax: tax), number_to_currency(tax_hash_gross[tax] - tax_hash_net[tax])]
|
|
||||||
end
|
|
||||||
unless marge == 0
|
|
||||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.markup_included', marge: marge), number_to_currency(total_gross * marge / 100.0)]
|
|
||||||
end
|
|
||||||
end_sum = total_gross * (1 + marge / 100.0)
|
|
||||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_gross'), number_to_currency(end_sum)]
|
|
||||||
# table for sum
|
|
||||||
table sum, position: :right, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
|
||||||
sum.length.times do |count|
|
|
||||||
table.row(count).columns(0..5).borders = []
|
|
||||||
end
|
|
||||||
table.row(sum.length - 1).columns(0..4).borders = []
|
|
||||||
table.row(sum.length - 1).border_bottom_width = 2
|
|
||||||
table.row(sum.length - 1).columns(5).borders = [:bottom]
|
|
||||||
end
|
|
||||||
|
|
||||||
if(FoodsoftConfig[:group_order_invoices][:vat_exempt])
|
|
||||||
move_down 15
|
|
||||||
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
|
|
||||||
end
|
|
||||||
move_down 10
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -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
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
class NotifyGroupOrderInvoiceJob < ApplicationJob
|
|
||||||
def perform(group_order_invoice)
|
|
||||||
ordergroup = group_order_invoice.group_order.ordergroup
|
|
||||||
ordergroup.users.each do |user|
|
|
||||||
Mailer.deliver_now_with_user_locale user do
|
|
||||||
Mailer.group_order_invoice(group_order_invoice, user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,25 +0,0 @@
|
||||||
# Foodsoft-file import
|
|
||||||
class FoodsoftFile
|
|
||||||
# parses a string from a foodsoft-file
|
|
||||||
# returns two arrays with articles and outlisted_articles
|
|
||||||
# the parsed article is a simple hash
|
|
||||||
def self.parse(file, options = {})
|
|
||||||
SpreadsheetFile.parse file, options do |row, row_index|
|
|
||||||
next if row[2].blank?
|
|
||||||
|
|
||||||
article = { :order_number => row[1],
|
|
||||||
:name => row[2],
|
|
||||||
:note => row[3],
|
|
||||||
:manufacturer => row[4],
|
|
||||||
:origin => row[5],
|
|
||||||
:unit => row[6],
|
|
||||||
:price => row[7],
|
|
||||||
:tax => row[8],
|
|
||||||
:deposit => (row[9].nil? ? "0" : row[9]),
|
|
||||||
:unit_quantity => row[10],
|
|
||||||
:article_category => row[13] }
|
|
||||||
status = row[0] && row[0].strip.downcase == 'x' ? :outlisted : nil
|
|
||||||
yield status, article, row_index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,28 +2,60 @@ require 'csv'
|
||||||
|
|
||||||
class OrderCsv < RenderCsv
|
class OrderCsv < RenderCsv
|
||||||
def header
|
def header
|
||||||
[
|
params = @options[:custom_csv]
|
||||||
OrderArticle.human_attribute_name(:units_to_order),
|
arr = if params.nil?
|
||||||
Article.human_attribute_name(:order_number),
|
[
|
||||||
Article.human_attribute_name(:name),
|
OrderArticle.human_attribute_name(:units_to_order),
|
||||||
Article.human_attribute_name(:unit),
|
Article.human_attribute_name(:order_number),
|
||||||
Article.human_attribute_name(:unit_quantity_short),
|
Article.human_attribute_name(:name),
|
||||||
ArticlePrice.human_attribute_name(:price),
|
Article.human_attribute_name(:unit),
|
||||||
OrderArticle.human_attribute_name(:total_price)
|
Article.human_attribute_name(:unit_quantity_short),
|
||||||
]
|
ArticlePrice.human_attribute_name(: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
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ class RenderPdf < Prawn::Document
|
||||||
options[:skip_page_creation] = true
|
options[:skip_page_creation] = true
|
||||||
@options = options
|
@options = options
|
||||||
@first_page = true
|
@first_page = true
|
||||||
no_footer = @options&.[](:no_footer) ? true : false
|
|
||||||
super(options)
|
super(options)
|
||||||
|
|
||||||
# Use ttf for better utf-8 compability
|
# Use ttf for better utf-8 compability
|
||||||
|
@ -84,11 +84,11 @@ class RenderPdf < Prawn::Document
|
||||||
)
|
)
|
||||||
|
|
||||||
header = options[:title] || title
|
header = options[:title] || title
|
||||||
footer = I18n.l(Time.now, format: :long) unless no_footer
|
footer = I18n.l(Time.now, format: :long)
|
||||||
|
|
||||||
header_size = 0
|
header_size = 0
|
||||||
header_size = height_of(header, size: HEADER_FONT_SIZE, font: DEFAULT_FONT) + HEADER_SPACE if header
|
header_size = height_of(header, size: HEADER_FONT_SIZE, font: DEFAULT_FONT) + HEADER_SPACE if header
|
||||||
footer_size = no_footer ? 0 : height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE
|
footer_size = height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE
|
||||||
|
|
||||||
start_new_page(top_margin: TOP_MARGIN + header_size, bottom_margin: BOTTOM_MARGIN + footer_size)
|
start_new_page(top_margin: TOP_MARGIN + header_size, bottom_margin: BOTTOM_MARGIN + footer_size)
|
||||||
|
|
||||||
|
@ -98,15 +98,12 @@ class RenderPdf < Prawn::Document
|
||||||
bounding_box [bounds.left, bounds.top + header_size], width: bounds.width, height: header_size do
|
bounding_box [bounds.left, bounds.top + header_size], width: bounds.width, height: header_size do
|
||||||
text header, size: HEADER_FONT_SIZE, align: :center, overflow: :shrink_to_fit if header
|
text header, size: HEADER_FONT_SIZE, align: :center, overflow: :shrink_to_fit if header
|
||||||
end
|
end
|
||||||
|
font_size FOOTER_FONT_SIZE do
|
||||||
unless no_footer
|
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
||||||
font_size FOOTER_FONT_SIZE do
|
text footer, align: :left, valign: :bottom
|
||||||
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
end
|
||||||
text footer, align: :left, valign: :bottom
|
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
||||||
end
|
text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom
|
||||||
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
|
||||||
text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
require 'roo'
|
|
||||||
|
|
||||||
class SpreadsheetFile
|
|
||||||
def self.parse(file, options = {})
|
|
||||||
filepath = file.is_a?(String) ? file : file.to_path
|
|
||||||
filename = options.delete(:filename) || filepath
|
|
||||||
fileext = File.extname(filename)
|
|
||||||
options[:csv_options] = { col_sep: ';', encoding: 'utf-8' }.merge(options[:csv_options] || {})
|
|
||||||
s = Roo::Spreadsheet.open(filepath, options.merge({ extension: fileext }))
|
|
||||||
|
|
||||||
row_index = 1
|
|
||||||
s.each do |row|
|
|
||||||
if row_index == 1
|
|
||||||
# @todo try to detect headers; for now using the index is ok
|
|
||||||
else
|
|
||||||
yield row, row_index
|
|
||||||
end
|
|
||||||
row_index += 1
|
|
||||||
end
|
|
||||||
row_index
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -51,18 +51,6 @@ class Mailer < ActionMailer::Base
|
||||||
subject: I18n.t('mailer.welcome.subject')
|
subject: I18n.t('mailer.welcome.subject')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends automatically generated invoicesfor group orders to ordergroup members
|
|
||||||
def group_order_invoice(group_order_invoice, user)
|
|
||||||
@user = user
|
|
||||||
@group_order_invoice = group_order_invoice
|
|
||||||
@group_order = group_order_invoice.group_order
|
|
||||||
@supplier = @group_order.order.supplier.name
|
|
||||||
@group = @group_order.ordergroup
|
|
||||||
add_group_order_invoice_attachments(group_order_invoice)
|
|
||||||
mail to: user,
|
|
||||||
subject: I18n.t('mailer.group_order_invoice.subject', group: @group.name, supplier: @supplier)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sends order result for specific Ordergroup
|
# Sends order result for specific Ordergroup
|
||||||
def order_result(user, group_order)
|
def order_result(user, group_order)
|
||||||
@order = group_order.order
|
@order = group_order.order
|
||||||
|
@ -180,11 +168,6 @@ class Mailer < ActionMailer::Base
|
||||||
attachments['order.csv'] = OrderCsv.new(order, options).to_csv
|
attachments['order.csv'] = OrderCsv.new(order, options).to_csv
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_group_order_invoice_attachments(group_order_invoice)
|
|
||||||
attachment_name = group_order_invoice.name + '.pdf'
|
|
||||||
attachments[attachment_name] = GroupOrderInvoicePdf.new(group_order_invoice.load_data_for_invoice).to_pdf
|
|
||||||
end
|
|
||||||
|
|
||||||
# separate method to allow plugins to mess with the text
|
# separate method to allow plugins to mess with the text
|
||||||
def additonal_welcome_text(user)
|
def additonal_welcome_text(user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -143,20 +143,24 @@ class Article < ApplicationRecord
|
||||||
new_unit = new_article.unit
|
new_unit = new_article.unit
|
||||||
end
|
end
|
||||||
|
|
||||||
return Article.compare_attributes(
|
attribute_hash = {
|
||||||
{
|
:name => [self.name, new_article.name],
|
||||||
:name => [self.name, new_article.name],
|
:manufacturer => [self.manufacturer, new_article.manufacturer.to_s],
|
||||||
:manufacturer => [self.manufacturer, new_article.manufacturer.to_s],
|
:origin => [self.origin, new_article.origin],
|
||||||
:origin => [self.origin, new_article.origin],
|
:unit => [self.unit, new_unit],
|
||||||
:unit => [self.unit, new_unit],
|
:price => [self.price.to_f.round(2), new_price.to_f.round(2)],
|
||||||
:price => [self.price.to_f.round(2), new_price.to_f.round(2)],
|
:tax => [self.tax, new_article.tax],
|
||||||
:tax => [self.tax, new_article.tax],
|
:deposit => [self.deposit.to_f.round(2), new_article.deposit.to_f.round(2)],
|
||||||
:deposit => [self.deposit.to_f.round(2), new_article.deposit.to_f.round(2)],
|
# take care of different num-objects.
|
||||||
# take care of different num-objects.
|
:unit_quantity => [self.unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f],
|
||||||
:unit_quantity => [self.unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f],
|
:note => [self.note.to_s, new_article.note.to_s]
|
||||||
:note => [self.note.to_s, new_article.note.to_s]
|
}
|
||||||
}
|
if options[:update_category] == true
|
||||||
)
|
new_article_category = new_article.article_category
|
||||||
|
attribute_hash[:article_category] = [self.article_category, new_article_category] unless new_article_category.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
Article.compare_attributes(attribute_hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Compare attributes from two different articles.
|
# Compare attributes from two different articles.
|
||||||
|
|
|
@ -8,7 +8,7 @@ module LocalizeInput
|
||||||
separator = I18n.t("separator", scope: "number.format")
|
separator = I18n.t("separator", scope: "number.format")
|
||||||
delimiter = I18n.t("delimiter", scope: "number.format")
|
delimiter = I18n.t("delimiter", scope: "number.format")
|
||||||
input.gsub!(delimiter, "") if input.match(/\d+#{Regexp.escape(delimiter)}+\d+#{Regexp.escape(separator)}+\d+/) # Remove delimiter
|
input.gsub!(delimiter, "") if input.match(/\d+#{Regexp.escape(delimiter)}+\d+#{Regexp.escape(separator)}+\d+/) # Remove delimiter
|
||||||
input.gsub!(separator, ".") # Replace separator with db compatible character
|
input.gsub!(separator, ".") or input.gsub!(",", ".") # Replace separator with db compatible character
|
||||||
input
|
input
|
||||||
rescue
|
rescue
|
||||||
Rails.logger.warn "Can't localize input: #{input}"
|
Rails.logger.warn "Can't localize input: #{input}"
|
||||||
|
|
|
@ -9,7 +9,6 @@ class GroupOrder < ApplicationRecord
|
||||||
has_many :group_order_articles, :dependent => :destroy
|
has_many :group_order_articles, :dependent => :destroy
|
||||||
has_many :order_articles, :through => :group_order_articles
|
has_many :order_articles, :through => :group_order_articles
|
||||||
has_one :financial_transaction
|
has_one :financial_transaction
|
||||||
has_one :group_order_invoice
|
|
||||||
belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id'
|
belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id'
|
||||||
|
|
||||||
validates_presence_of :order_id
|
validates_presence_of :order_id
|
||||||
|
@ -33,8 +32,8 @@ class GroupOrder < ApplicationRecord
|
||||||
# Generate some data for the javascript methods in ordering view
|
# Generate some data for the javascript methods in ordering view
|
||||||
def load_data
|
def load_data
|
||||||
data = {}
|
data = {}
|
||||||
data[:account_balance] = ordergroup.nil? ? BigDecimal.new('+Infinity') : ordergroup.account_balance
|
data[:account_balance] = ordergroup.nil? ? BigDecimal('+Infinity') : ordergroup.account_balance
|
||||||
data[:available_funds] = ordergroup.nil? ? BigDecimal.new('+Infinity') : ordergroup.get_available_funds(self)
|
data[:available_funds] = ordergroup.nil? ? BigDecimal('+Infinity') : ordergroup.get_available_funds(self)
|
||||||
|
|
||||||
# load prices and other stuff....
|
# load prices and other stuff....
|
||||||
data[:order_articles] = {}
|
data[:order_articles] = {}
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
class GroupOrderInvoice < ApplicationRecord
|
|
||||||
belongs_to :group_order
|
|
||||||
validates_presence_of :group_order
|
|
||||||
validates_uniqueness_of :invoice_number
|
|
||||||
validate :tax_number_set
|
|
||||||
after_initialize :init, unless: :persisted?
|
|
||||||
|
|
||||||
def generate_invoice_number(count)
|
|
||||||
trailing_number = count.to_s.rjust(4, '0')
|
|
||||||
if GroupOrderInvoice.find_by(invoice_number: self.invoice_date.strftime("%Y%m%d") + trailing_number)
|
|
||||||
generate_invoice_number(count.to_i + 1)
|
|
||||||
else
|
|
||||||
self.invoice_date.strftime("%Y%m%d") + trailing_number
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def tax_number_set
|
|
||||||
if FoodsoftConfig[:contact][:tax_number].blank?
|
|
||||||
errors.add(:group_order_invoice, "Keine Steuernummer in FoodsoftConfig :contact gesetzt")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def init
|
|
||||||
self.invoice_date = Time.now unless invoice_date
|
|
||||||
self.invoice_number = generate_invoice_number(1) unless self.invoice_number
|
|
||||||
self.payment_method = FoodsoftConfig[:group_order_invoices]&.[](:payment_method) || I18n.t('activerecord.attributes.group_order_invoice.payment_method') unless self.payment_method
|
|
||||||
end
|
|
||||||
|
|
||||||
def name
|
|
||||||
I18n.t('activerecord.attributes.group_order_invoice.name') + "_#{invoice_number}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_data_for_invoice
|
|
||||||
invoice_data = {}
|
|
||||||
order = group_order.order
|
|
||||||
invoice_data[:supplier] = order.supplier.name
|
|
||||||
invoice_data[:ordergroup] = group_order.ordergroup
|
|
||||||
invoice_data[:group_order] = group_order
|
|
||||||
invoice_data[:invoice_number] = invoice_number
|
|
||||||
invoice_data[:invoice_date] = invoice_date
|
|
||||||
invoice_data[:tax_number] = FoodsoftConfig[:contact][:tax_number]
|
|
||||||
invoice_data[:payment_method] = payment_method
|
|
||||||
invoice_data[:order_articles] = {}
|
|
||||||
group_order.order_articles.each do |order_article|
|
|
||||||
# Get the result of last time ordering, if possible
|
|
||||||
goa = group_order.group_order_articles.detect { |tmp_goa| tmp_goa.order_article_id == order_article.id }
|
|
||||||
|
|
||||||
# Build hash with relevant data
|
|
||||||
invoice_data[:order_articles][order_article.id] = {
|
|
||||||
:price => order_article.article.fc_price,
|
|
||||||
:quantity => (goa ? goa.quantity : 0),
|
|
||||||
:total_price => (goa ? goa.total_price : 0),
|
|
||||||
:tax => order_article.article.tax
|
|
||||||
}
|
|
||||||
end
|
|
||||||
invoice_data
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
require 'foodsoft_article_import'
|
||||||
class Supplier < ApplicationRecord
|
class Supplier < ApplicationRecord
|
||||||
include MarkAsDeletedWithName
|
include MarkAsDeletedWithName
|
||||||
include CustomFields
|
include CustomFields
|
||||||
|
@ -73,15 +74,24 @@ class Supplier < ApplicationRecord
|
||||||
# Synchronise articles with spreadsheet.
|
# Synchronise articles with spreadsheet.
|
||||||
#
|
#
|
||||||
# @param file [File] Spreadsheet file to parse
|
# @param file [File] Spreadsheet file to parse
|
||||||
# @param options [Hash] Options passed to {FoodsoftFile#parse} except when listed here.
|
# @param options [Hash] Options passed to {FoodsoftArticleImport#parse} except when listed here.
|
||||||
# @option options [Boolean] :outlist_absent Set to +true+ to remove articles not in spreadsheet.
|
# @option options [Boolean] :outlist_absent Set to +true+ to remove articles not in spreadsheet.
|
||||||
# @option options [Boolean] :convert_units Omit or set to +true+ to keep current units, recomputing unit quantity and price.
|
# @option options [Boolean] :convert_units Omit or set to +true+ to keep current units, recomputing unit quantity and price.
|
||||||
def sync_from_file(file, options = {})
|
def sync_from_file(file, type, options = {})
|
||||||
all_order_numbers = []
|
all_order_numbers = []
|
||||||
updated_article_pairs, outlisted_articles, new_articles = [], [], []
|
updated_article_pairs, outlisted_articles, new_articles = [], [], []
|
||||||
FoodsoftFile::parse file, options do |status, new_attrs, line|
|
custom_codes_path = File.join(Rails.root, "config", "custom_codes.yml")
|
||||||
|
opts = options.except(:convert_units, :outlist_absent, :update_category)
|
||||||
|
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|
|
||||||
article = articles.undeleted.where(order_number: new_attrs[:order_number]).first
|
article = articles.undeleted.where(order_number: new_attrs[:order_number]).first
|
||||||
new_attrs[:article_category] = ArticleCategory.find_match(new_attrs[:article_category])
|
|
||||||
|
if new_attrs[:article_category].present? && options[:update_category]
|
||||||
|
new_attrs[:article_category] = ArticleCategory.find_match(new_attrs[:article_category]) || ArticleCategory.create_or_find_by!(name: new_attrs[:article_category])
|
||||||
|
else
|
||||||
|
new_attrs[:article_category] = nil
|
||||||
|
end
|
||||||
|
|
||||||
new_attrs[:tax] ||= FoodsoftConfig[:tax_default]
|
new_attrs[:tax] ||= FoodsoftConfig[:tax_default]
|
||||||
new_article = articles.build(new_attrs)
|
new_article = articles.build(new_attrs)
|
||||||
|
|
||||||
|
@ -89,7 +99,7 @@ class Supplier < ApplicationRecord
|
||||||
if article.nil?
|
if article.nil?
|
||||||
new_articles << new_article
|
new_articles << new_article
|
||||||
else
|
else
|
||||||
unequal_attributes = article.unequal_attributes(new_article, options.slice(:convert_units))
|
unequal_attributes = article.unequal_attributes(new_article, options.slice(:convert_units, :update_category))
|
||||||
unless unequal_attributes.empty?
|
unless unequal_attributes.empty?
|
||||||
article.attributes = unequal_attributes
|
article.attributes = unequal_attributes
|
||||||
updated_article_pairs << [article, unequal_attributes]
|
updated_article_pairs << [article, unequal_attributes]
|
||||||
|
|
|
@ -7,5 +7,4 @@
|
||||||
= config_input c, :country, as: :string, input_html: {class: 'input-xlarge'}
|
= config_input c, :country, as: :string, input_html: {class: 'input-xlarge'}
|
||||||
= config_input c, :email, required: true, input_html: {class: 'input-xlarge'}
|
= config_input c, :email, required: true, input_html: {class: 'input-xlarge'}
|
||||||
= config_input c, :phone, input_html: {class: 'input-medium'}
|
= config_input c, :phone, input_html: {class: 'input-medium'}
|
||||||
= config_input c, :tax_number, input_html: {class: 'input-medium'}
|
|
||||||
= config_input form, :homepage, required: true, as: :url, input_html: {class: 'input-xlarge'}
|
= config_input form, :homepage, required: true, as: :url, input_html: {class: 'input-xlarge'}
|
||||||
|
|
|
@ -13,11 +13,6 @@
|
||||||
= config_input form, :charge_members_manually, as: :boolean
|
= config_input form, :charge_members_manually, as: :boolean
|
||||||
= config_input form, :use_iban, as: :boolean
|
= config_input form, :use_iban, as: :boolean
|
||||||
= config_input form, :use_self_service, as: :boolean
|
= config_input form, :use_self_service, as: :boolean
|
||||||
%h4= t '.group_order_invoices'
|
|
||||||
= form.fields_for :group_order_invoices do |field|
|
|
||||||
= config_input field, :use_automatic_invoices, as: :boolean
|
|
||||||
= config_input field, :vat_exempt, as: :boolean
|
|
||||||
= config_input field, :payment_method, as: :string, input_html: {class: 'input-medium'}
|
|
||||||
|
|
||||||
%h4= t '.schedule_title'
|
%h4= t '.schedule_title'
|
||||||
= form.simple_fields_for :order_schedule do |fields|
|
= form.simple_fields_for :order_schedule do |fields|
|
||||||
|
|
|
@ -49,7 +49,8 @@
|
||||||
.input-prepend
|
.input-prepend
|
||||||
%span.add-on= t 'number.currency.format.unit'
|
%span.add-on= t 'number.currency.format.unit'
|
||||||
= form.text_field 'deposit', class: 'input-mini', style: 'width: 45px'
|
= form.text_field 'deposit', class: 'input-mini', style: 'width: 45px'
|
||||||
%td= form.select :article_category_id, ArticleCategory.all.map {|a| [ a.name, a.id ] },
|
%td{:style => highlight_new(attrs, :article_category)}
|
||||||
|
= form.select :article_category_id, ArticleCategory.all.map {|a| [ a.name, a.id ] },
|
||||||
{include_blank: true}, class: 'input-small'
|
{include_blank: true}, class: 'input-small'
|
||||||
- unless changed_article.errors.empty?
|
- unless changed_article.errors.empty?
|
||||||
%tr.alert
|
%tr.alert
|
||||||
|
|
|
@ -71,11 +71,19 @@
|
||||||
= form_for :articles, :url => parse_upload_supplier_articles_path(@supplier),
|
= form_for :articles, :url => parse_upload_supplier_articles_path(@supplier),
|
||||||
:html => { multipart: true, class: "form-horizontal" } do |f|
|
:html => { multipart: true, class: "form-horizontal" } do |f|
|
||||||
|
|
||||||
.control-group
|
|
||||||
%label(for="articles_file")= t '.file_label'
|
|
||||||
= f.file_field "file"
|
|
||||||
|
|
||||||
.control-group
|
.control-group
|
||||||
|
%label(for="articles_file")
|
||||||
|
%strong= t '.file_label'
|
||||||
|
= f.file_field "file"
|
||||||
|
%label(for="articles_file")
|
||||||
|
%strong="select the file type you are about to upload"
|
||||||
|
=f.collection_select :type, ["bnn","foodsoft","odin"], :to_s, :to_s
|
||||||
|
|
||||||
|
.control-group
|
||||||
|
%label(for="articles_update_category")
|
||||||
|
= f.check_box "update_category"
|
||||||
|
= t '.options.update_category'
|
||||||
%label(for="articles_outlist_absent")
|
%label(for="articles_outlist_absent")
|
||||||
= f.check_box "outlist_absent"
|
= f.check_box "outlist_absent"
|
||||||
= t '.options.outlist_absent'
|
= t '.options.outlist_absent'
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
%th= t('.end')
|
%th= t('.end')
|
||||||
%th= t('.state')
|
%th= t('.state')
|
||||||
%th= heading_helper Order, :updated_by
|
%th= heading_helper Order, :updated_by
|
||||||
%th= heading_helper GroupOrderInvoice, :name
|
|
||||||
%th
|
|
||||||
%th
|
%th
|
||||||
%tbody
|
%tbody
|
||||||
- @orders.each do |order|
|
- @orders.each do |order|
|
||||||
|
@ -19,14 +17,6 @@
|
||||||
%td=h format_time(order.ends) unless order.ends.nil?
|
%td=h format_time(order.ends) unless order.ends.nil?
|
||||||
%td= order.closed? ? t('.cleared', amount: number_to_currency(order.foodcoop_result)) : t('.ended')
|
%td= order.closed? ? t('.cleared', amount: number_to_currency(order.foodcoop_result)) : t('.ended')
|
||||||
%td= show_user(order.updated_by)
|
%td= show_user(order.updated_by)
|
||||||
%td{id: "generate-invoice#{order.id}"}
|
|
||||||
- if order.closed?
|
|
||||||
-if FoodsoftConfig[:contact][:tax_number] && order.ordergroups.present?
|
|
||||||
= render :partial => 'group_order_invoices/links', locals:{order: order}
|
|
||||||
-else
|
|
||||||
= I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set')
|
|
||||||
- else
|
|
||||||
= t('orders.index.not_closed')
|
|
||||||
%td
|
%td
|
||||||
- unless order.closed?
|
- unless order.closed?
|
||||||
- if current_user.role_orders?
|
- if current_user.role_orders?
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- title t('.title')
|
- title t('.title')
|
||||||
- puts params
|
|
||||||
- content_for :actionbar do
|
- content_for :actionbar do
|
||||||
- if FoodsoftConfig[:charge_members_manually]
|
- if FoodsoftConfig[:charge_members_manually]
|
||||||
= link_to t('.close_all_direct_with_invoice'), close_all_direct_with_invoice_finance_order_index_path, method: :post, class: 'btn'
|
= link_to t('.close_all_direct_with_invoice'), close_all_direct_with_invoice_finance_order_index_path, method: :post, class: 'btn'
|
||||||
|
|
|
@ -22,3 +22,12 @@
|
||||||
%td
|
%td
|
||||||
= link_to t('.new_transaction'), new_finance_ordergroup_transaction_path(ordergroup), class: 'btn btn-mini'
|
= link_to t('.new_transaction'), new_finance_ordergroup_transaction_path(ordergroup), class: 'btn btn-mini'
|
||||||
= link_to t('.account_statement'), finance_ordergroup_transactions_path(ordergroup), class: 'btn btn-mini'
|
= link_to t('.account_statement'), finance_ordergroup_transactions_path(ordergroup), class: 'btn btn-mini'
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th= t 'Total'
|
||||||
|
%th
|
||||||
|
- FinancialTransactionClass.sorted.each do |c|
|
||||||
|
- name = FinancialTransactionClass.has_multiple_classes ? c.display : heading_helper(Ordergroup, :account_balance)
|
||||||
|
%th.numeric= format_currency @total_balances[c.id]
|
||||||
|
%th.numeric
|
||||||
|
= format_currency @total_balances.values.reduce(:+)
|
|
@ -1,25 +0,0 @@
|
||||||
.row
|
|
||||||
.column.small-12
|
|
||||||
- show_generate_with_date = true
|
|
||||||
- order.group_orders.each do |go|
|
|
||||||
- if go.group_order_invoice.present?
|
|
||||||
- show_generate_with_date = false
|
|
||||||
- if show_generate_with_date
|
|
||||||
= form_for :group_order_invoice, url: url_for('group_order_invoice#create_multiple'), remote: true do |f|
|
|
||||||
= f.label :invoice_date, I18n.t('activerecord.attributes.group_order_invoice.links.invoice_date')
|
|
||||||
= f.date_field :invoice_date, {value: Date.today, max: Date.today, required: true}
|
|
||||||
= f.hidden_field :order_id, value: order.id
|
|
||||||
= f.submit I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date'), class: 'btn btn small'
|
|
||||||
|
|
||||||
- order.group_orders.includes([:group_order_invoice, :ordergroup]).each do |go|
|
|
||||||
.row
|
|
||||||
.column.small-3
|
|
||||||
= label_tag go.ordergroup.name
|
|
||||||
- if go.group_order_invoice
|
|
||||||
.column.small-3
|
|
||||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.download'), group_order_invoice_path(go.group_order_invoice, :format => 'pdf'), class: 'btn btn-small'
|
|
||||||
.column.small-3
|
|
||||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), go.group_order_invoice, method: :delete, class: 'btn btn-danger btn-small', remote: true
|
|
||||||
- else
|
|
||||||
= button_to I18n.t('activerecord.attributes.group_order_invoice.links.generate'), group_order_invoices_path(:method => :post, group_order: go) ,class: 'btn btn-small', params: {id: order.id}, remote: true
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
$("#generate-invoice<%= params[:id] %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
|
|
@ -1 +0,0 @@
|
||||||
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
|
|
@ -1 +0,0 @@
|
||||||
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
|
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'
|
|
@ -11,170 +11,142 @@
|
||||||
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
|
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
|
||||||
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
|
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
|
||||||
new List(document.body, {
|
new List(document.body, {
|
||||||
valueNames: ['name'],
|
valueNames: ['name'],
|
||||||
engine: 'unlist',
|
engine: 'unlist',
|
||||||
plugins: [listjsResetPlugin, listjsDelayPlugin],
|
plugins: [listjsResetPlugin, listjsDelayPlugin],
|
||||||
// make large pages work too (as we don't have paging - articles may disappear!)
|
// make large pages work too (as we don't have paging - articles may disappear!)
|
||||||
page: 10000,
|
page: 10000,
|
||||||
indexAsync: true
|
indexAsync: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
- 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
|
= render 'switch_order', current_order: @order
|
||||||
%dl.dl-horizontal
|
.well
|
||||||
- unless @order.note.blank?
|
= render 'explanations'
|
||||||
%dt= heading_helper Order, :note
|
.well.span9
|
||||||
%dd= @order.note
|
%h2.span9= t '.sub_title', order_name: @order.name
|
||||||
%dt= heading_helper Order, :created_by
|
.span3
|
||||||
%dd= show_user_link(@order.created_by)
|
%table.table-condensed
|
||||||
%dt= heading_helper Order, :ends
|
-if @order.ends
|
||||||
%dd= format_time(@order.ends)
|
%tr
|
||||||
%dt= heading_helper Order, :pickup
|
%td= heading_helper(Order, :ends) + ': '
|
||||||
%dd= format_date(@order.pickup)
|
%td= format_time(@order.ends)
|
||||||
- unless @order.stockit? or @order.supplier.min_order_quantity.blank?
|
- unless @order.stockit? or @order.supplier.min_order_quantity.blank?
|
||||||
%dt= heading_helper Supplier, :min_order_quantity, short: true
|
%tr
|
||||||
%dd= @order.supplier.min_order_quantity
|
%td= heading_helper(Supplier, :min_order_quantity)
|
||||||
%dt= t '.sum_amount'
|
%td= number_to_currency(@order.supplier.min_order_quantity)
|
||||||
%dd= number_to_currency @order.sum
|
%tr
|
||||||
- unless @group_order.new_record?
|
%td= t('group_orders.form.sum_amount') + ':'
|
||||||
%dt= heading_helper GroupOrder, :updated_by
|
%td= number_to_currency(@order.sum)
|
||||||
%dd
|
%hr
|
||||||
= show_user(@group_order.updated_by)
|
.form-search.pull-right
|
||||||
(#{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
|
|
||||||
|
|
||||||
.row-fluid
|
|
||||||
.well.clear
|
|
||||||
.form-search
|
|
||||||
.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.table
|
%thead
|
||||||
%thead
|
%tr
|
||||||
%tr
|
%th= heading_helper Article, :name
|
||||||
%th= heading_helper Article, :name
|
|
||||||
- if @order.stockit?
|
|
||||||
%th{style: 'width:120px'}= heading_helper StockArticle, :supplier
|
|
||||||
%th{style: "width:13px;"}
|
|
||||||
%th{style: "width:4.5em;"}= t '.price'
|
|
||||||
%th{style: "width:4.5em;"}= heading_helper Article, :unit
|
|
||||||
- unless @order.stockit?
|
|
||||||
%th{style: "width:70px;"}= heading_helper OrderArticle, :missing_units, short: true
|
|
||||||
%th#col_required= heading_helper GroupOrderArticle, :quantity
|
|
||||||
%th#col_tolerance= heading_helper GroupOrderArticle, :tolerance
|
|
||||||
- else
|
|
||||||
%th(style="width:20px")= heading_helper StockArticle, :available
|
|
||||||
%th#col_required= heading_helper GroupOrderArticle, :quantity
|
|
||||||
%th{style: "width:15px;"}= heading_helper GroupOrderArticle, :total_price
|
|
||||||
%tbody.list
|
|
||||||
- @order.articles_grouped_by_category.each do |category, order_articles|
|
|
||||||
%tr.list-heading.article-category
|
|
||||||
%td
|
|
||||||
= category
|
|
||||||
%i.icon-tag
|
|
||||||
%td{colspan: "9"}
|
|
||||||
- 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", tabindex: "0"}
|
|
||||||
%td.name= order_article.article.name
|
|
||||||
- if @order.stockit?
|
- if @order.stockit?
|
||||||
%td= truncate order_article.article.supplier.name, length: 15
|
%th{style: 'width:120px'}= heading_helper StockArticle, :supplier
|
||||||
%td= h order_article.article.origin
|
%th{style: "width:13px;"}
|
||||||
%td= number_to_currency(@ordering_data[:order_articles][order_article.id][:price])
|
%th{style: "width:4.5em;"}= t '.price'
|
||||||
%td= order_article.article.unit
|
%th{style: "width:4.5em;"}= t '.price_per_base_unit'
|
||||||
%td
|
%th{style: "width:4.5em;"}= heading_helper Article, :unit
|
||||||
- if @order.stockit?
|
- unless @order.stockit?
|
||||||
= @ordering_data[:order_articles][order_article.id][:quantity_available]
|
%th{style: "width:70px;"}= heading_helper OrderArticle, :missing_units, short: true
|
||||||
- else
|
%th#col_required= heading_helper GroupOrderArticle, :quantity
|
||||||
%span{id: "missing_units_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:missing_units]
|
%th#col_tolerance= heading_helper GroupOrderArticle, :tolerance
|
||||||
|
- else
|
||||||
|
%th(style="width:20px")= heading_helper StockArticle, :available
|
||||||
|
%th#col_required= heading_helper GroupOrderArticle, :quantity
|
||||||
|
%th{style: "width:15px;"}= heading_helper GroupOrderArticle, :total_price
|
||||||
|
%tbody.list
|
||||||
|
- @order.articles_grouped_by_category.each do | category, order_articles|
|
||||||
|
%tr.list-heading.article-category
|
||||||
|
%td
|
||||||
|
= category
|
||||||
|
%i.icon-tag
|
||||||
|
%td{colspan: "9"}
|
||||||
|
- 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", tabindex: "0"}
|
||||||
|
%td.name= order_article.article.name
|
||||||
|
- if @order.stockit?
|
||||||
|
%td= truncate order_article.article.supplier.name, length: 15
|
||||||
|
%td= h order_article.article.origin
|
||||||
|
%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
|
||||||
|
- if @order.stockit?
|
||||||
|
= @ordering_data[:order_articles][order_article.id][:quantity_available]
|
||||||
|
- else
|
||||||
|
%span{id: "missing_units_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:missing_units]
|
||||||
|
|
||||||
%td.quantity
|
%td.quantity
|
||||||
%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?)}/
|
.outer{style: "diyplay: inline-block; float: left; width: 50px;"}
|
||||||
%span.used{id: "q_used_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:used_quantity]
|
%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.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
|
%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]
|
||||||
%a.btn.btn-ordering{'data-increase_quantity' => order_article.id}
|
.btn-group
|
||||||
%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?)}/
|
||||||
- if (@ordering_data[:order_articles][order_article.id][:unit] > 1)
|
- if (@ordering_data[:order_articles][order_article.id][:unit] > 1)
|
||||||
%span.used{id: "t_used_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:used_tolerance]
|
%span.used{id: "t_used_#{order_article.id}"}= @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]
|
%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}
|
%a.btn.btn-ordering{'data-decrease_tolerance' => order_article.id}
|
||||||
%i.icon-plus
|
%i.icon-minus
|
||||||
%a.btn.btn-ordering{'data-decrease_tolerance' => order_article.id}
|
%a.btn.btn-ordering{'data-increase_tolerance' => order_article.id}
|
||||||
%i.icon-minus
|
%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])
|
||||||
.article-info
|
.article-info
|
||||||
.article-name= order_article.article.name
|
.article-name= order_article.article.name
|
||||||
.pull-right
|
.pull-right
|
||||||
= t('.units_full') + ':'
|
= t('.units_full') + ':'
|
||||||
%span{id: "units_#{order_article.id}"}= order_article.units_to_order
|
%span{id: "units_#{order_article.id}"}= order_article.units_to_order
|
||||||
%br/
|
%br/
|
||||||
= t('.units_total') + ':'
|
= t('.units_total') + ':'
|
||||||
%span{id: "q_total_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:quantity] + @ordering_data[:order_articles][order_article.id][:others_quantity]
|
%span{id: "q_total_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:quantity] + @ordering_data[:order_articles][order_article.id][:others_quantity]
|
||||||
%br/
|
%br/
|
||||||
= t('.total_tolerance') + ':'
|
= t('.total_tolerance') + ':'
|
||||||
%span{id: "t_total_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:tolerance] + @ordering_data[:order_articles][order_article.id][:others_tolerance]
|
%span{id: "t_total_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:tolerance] + @ordering_data[:order_articles][order_article.id][:others_tolerance]
|
||||||
%br/
|
%br/
|
||||||
.pull-left
|
.pull-left
|
||||||
#{heading_helper Article, :manufacturer}: #{order_article.article.manufacturer}
|
#{heading_helper Article, :manufacturer}: #{order_article.article.manufacturer}
|
||||||
%br/
|
%br/
|
||||||
#{heading_helper Article, :units}: #{@order.stockit? ? order_article.article.quantity_available : @ordering_data[:order_articles][order_article.id][:unit]} * #{h order_article.article.unit}
|
#{heading_helper Article, :units}: #{@order.stockit? ? order_article.article.quantity_available : @ordering_data[:order_articles][order_article.id][:unit]} * #{h order_article.article.unit}
|
||||||
%br/
|
%br/
|
||||||
#{heading_helper Article, :note}: #{order_article.article.note}
|
#{heading_helper Article, :note}: #{order_article.article.note}
|
||||||
%br/
|
%br/
|
||||||
#order-footer
|
#order-footer
|
||||||
#info-box
|
#info-box
|
||||||
#total-sum
|
#total-sum
|
||||||
%table
|
= render 'total_sum'
|
||||||
%tr
|
#order-button
|
||||||
%td= t('.total_sum_amount') + ':'
|
= submit_tag( t('.action_save'), id: 'submit_button', class: 'btn btn-primary' )
|
||||||
%td.currency
|
#{link_to t('ui.or_cancel'), group_orders_path}
|
||||||
%span#total_price= number_to_currency(@group_order.price)
|
%input#total_balance{name: "total_balance", type: "hidden", value: @ordergroup.account_balance - @group_order.price}/
|
||||||
%tr
|
%input{name: "version", type: "hidden", value: @version}/
|
||||||
- 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
|
|
||||||
= submit_tag( t('.action_save'), id: 'submit_button', class: 'btn btn-primary' )
|
|
||||||
#{link_to t('ui.or_cancel'), group_orders_path}
|
|
||||||
%input#total_balance{name: "total_balance", type: "hidden", value: @ordergroup.account_balance - @group_order.price}/
|
|
||||||
%input{name: "version", type: "hidden", value: @version}/
|
|
||||||
|
|
|
@ -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,22 +18,27 @@
|
||||||
%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)
|
||||||
|
|
||||||
= render :partial => "shared/open_orders", :locals => {:ordergroup => @ordergroup}
|
.row-fluid
|
||||||
|
.span9
|
||||||
// finished orders
|
= render :partial => "shared/open_orders", :locals => {:ordergroup => @ordergroup}
|
||||||
|
// finished orders
|
||||||
- unless @finished_not_closed_orders_including_group_order.empty?
|
- unless @finished_not_closed_orders_including_group_order.empty?
|
||||||
%section
|
.row-fluid
|
||||||
%h2= t '.finished_orders.title'
|
.span9
|
||||||
= render partial: 'orders', locals: {orders: @finished_not_closed_orders_including_group_order, pagination: false}
|
%section
|
||||||
- if @ordergroup.value_of_finished_orders > 0
|
%h2= t '.finished_orders.title'
|
||||||
%p
|
= render partial: 'orders', locals: {orders: @finished_not_closed_orders_including_group_order, pagination: false}
|
||||||
= t('.finished_orders.total_sum') + ':'
|
- if @ordergroup.value_of_finished_orders > 0
|
||||||
%b= number_to_currency(@ordergroup.value_of_finished_orders)
|
%p
|
||||||
|
= t('.finished_orders.total_sum') + ':'
|
||||||
|
%b= number_to_currency(@ordergroup.value_of_finished_orders)
|
||||||
|
|
||||||
// closed orders
|
// closed orders
|
||||||
- unless @closed_orders_including_group_order.empty?
|
- unless @closed_orders_including_group_order.empty?
|
||||||
%section
|
.row-fluid
|
||||||
%h2= t '.closed_orders.title'
|
.span9
|
||||||
= render partial: 'orders', locals: {orders: @closed_orders_including_group_order, pagination: false}
|
%section
|
||||||
%br/
|
%h2= t '.closed_orders.title'
|
||||||
= link_to t('.closed_orders.more'), archive_group_orders_path
|
= render partial: 'orders', locals: {orders: @closed_orders_including_group_order, pagination: false}
|
||||||
|
%br/
|
||||||
|
= link_to t('.closed_orders.more'), archive_group_orders_path
|
||||||
|
|
|
@ -7,107 +7,115 @@
|
||||||
- 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)
|
||||||
%dt= heading_helper Order, :pickup
|
// Pickup
|
||||||
%dd= format_date(@order.pickup)
|
- unless @order.pickup.blank?
|
||||||
%dt= heading_helper GroupOrder, :price
|
%dt= heading_helper Order, :pickup
|
||||||
%dd
|
%dd= format_date(@order.pickup)
|
||||||
- if @group_order
|
// Min Order Quantity
|
||||||
= number_to_currency(@group_order.price)
|
- unless @order.stockit? or @order.supplier.min_order_quantity.blank?
|
||||||
- else
|
%dt= heading_helper Supplier, :min_order_quantity, short: true
|
||||||
= t '.not_ordered'
|
%dd= @order.supplier.min_order_quantity
|
||||||
- if @group_order && @group_order.transport
|
// Group Order Sum Amount
|
||||||
%dt= heading_helper GroupOrder, :transport
|
%dt= t 'group_orders.form.sum_amount'
|
||||||
%dd= number_to_currency(@group_order.transport)
|
%dd= number_to_currency @order.sum
|
||||||
%dt= heading_helper GroupOrder, :total
|
// Created By
|
||||||
%dd= number_to_currency(@group_order.total)
|
%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
|
||||||
|
= show_user(@group_order.updated_by)
|
||||||
|
(#{format_time(@group_order.updated_on)})
|
||||||
|
// Closed By
|
||||||
- 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?
|
||||||
|
%dt= heading_helper Order, :note
|
||||||
|
%dd= @order.note
|
||||||
|
|
||||||
.well.pull-right
|
// Article box
|
||||||
= close_button :alert
|
%section
|
||||||
= render 'switch_order', current_order: @order
|
.column_content#result
|
||||||
|
- if @group_order
|
||||||
// Article box
|
%p= link_to t('.articles.show_hide'), '#', 'data-toggle-this' => 'tr.ignored'
|
||||||
%section
|
%table.table.table-hover
|
||||||
%h2= t '.articles.title'
|
%thead
|
||||||
.column_content#result
|
%tr
|
||||||
- if @group_order
|
%th{style: "width:40%"}= heading_helper Article, :name
|
||||||
%p.pull-right= link_to t('.articles.show_hide'), '#', 'data-toggle-this' => 'tr.ignored'
|
%th= heading_helper Article, :units
|
||||||
%p= link_to(t('.articles.edit_order'), edit_group_order_path(@group_order, order_id: @order.id), class: 'btn btn-primary') if @order.open?
|
%th= t '.articles.unit_price'
|
||||||
%table.table.table-hover
|
%th
|
||||||
%thead
|
%abbr{title: t('.articles.ordered_title')}= t '.articles.ordered'
|
||||||
%tr
|
%th
|
||||||
%th{style: "width:40%"}= heading_helper Article, :name
|
%abbr{title: t('.articles.order_nopen_title')}
|
||||||
%th= heading_helper Article, :units
|
- if @order.open?
|
||||||
%th= t '.articles.unit_price'
|
= t '.articles.order_open'
|
||||||
%th
|
- else
|
||||||
%abbr{title: t('.articles.ordered_title')}= t '.articles.ordered'
|
= t '.articles.order_not_open'
|
||||||
%th
|
%th= heading_helper GroupOrderArticle, :total_price
|
||||||
%abbr{title: t('.articles.order_nopen_title')}
|
%tbody
|
||||||
- if @order.open?
|
- for category_name, order_articles in @order.articles_grouped_by_category
|
||||||
= t '.articles.order_open'
|
%tr.article-category
|
||||||
- else
|
%td
|
||||||
= t '.articles.order_not_open'
|
= category_name
|
||||||
%th= heading_helper GroupOrderArticle, :total_price
|
%i.icon-tag
|
||||||
%tbody
|
%td{colspan: "9"}
|
||||||
- for category_name, order_articles in @order.articles_grouped_by_category
|
- order_articles.each do |oa|
|
||||||
%tr.article-category
|
- # get the order-results for the ordergroup
|
||||||
%td
|
- r = get_order_results(oa, @group_order.id)
|
||||||
= category_name
|
%tr{class: cycle('even', 'odd', name: 'articles') + " " + order_article_class_name(r[:quantity], r[:tolerance], r[:result])}
|
||||||
%i.icon-tag
|
%td{style: "width:40%"}
|
||||||
%td{colspan: "9"}
|
= oa.article.name
|
||||||
- order_articles.each do |oa|
|
- unless oa.article.note.blank?
|
||||||
- # get the order-results for the ordergroup
|
= image_tag("lamp_grey.png", {alt: t('.articles.show_note'), size: "15x16", border: "0", onmouseover: "$('#note_#{oa.id}').show();", onmouseout: "$('#note_#{oa.id}').hide();"})
|
||||||
- r = get_order_results(oa, @group_order.id)
|
%td= "#{oa.price.unit_quantity} x #{oa.article.unit}"
|
||||||
%tr{class: cycle('even', 'odd', name: 'articles') + " " + order_article_class_name(r[:quantity], r[:tolerance], r[:result])}
|
%td= number_to_currency(oa.price.fc_price)
|
||||||
%td{style: "width:40%"}
|
%td
|
||||||
= oa.article.name
|
= r[:quantity]
|
||||||
|
= "+ #{r[:tolerance]}" if oa.price.unit_quantity > 1
|
||||||
|
%td= r[:result] > 0 ? r[:result] : "0"
|
||||||
|
%td= number_to_currency(r[:sub_total])
|
||||||
- unless oa.article.note.blank?
|
- unless oa.article.note.blank?
|
||||||
= image_tag("lamp_grey.png", {alt: t('.articles.show_note'), size: "15x16", border: "0", onmouseover: "$('#note_#{oa.id}').show();", onmouseout: "$('#note_#{oa.id}').hide();"})
|
%tr{id: "note_#{oa.id}", class: "note even", style: "display:none"}
|
||||||
%td= "#{oa.price.unit_quantity} x #{oa.article.unit}"
|
%td{colspan: "6"}=h oa.article.note
|
||||||
%td= number_to_currency(oa.price.fc_price)
|
%tr{class: cycle('even', 'odd', name: 'articles')}
|
||||||
%td
|
%th{colspan: "5"}= heading_helper GroupOrder, :price
|
||||||
= r[:quantity]
|
%th= number_to_currency(@group_order.price)
|
||||||
= "+ #{r[:tolerance]}" if oa.price.unit_quantity > 1
|
- if @group_order.transport
|
||||||
%td= r[:result] > 0 ? r[:result] : "0"
|
%tr{class: cycle('even', 'odd', name: 'articles')}
|
||||||
%td= number_to_currency(r[:sub_total])
|
%td{colspan: "5"}= heading_helper GroupOrder, :transport
|
||||||
- unless oa.article.note.blank?
|
%td= number_to_currency(@group_order.transport)
|
||||||
%tr{id: "note_#{oa.id}", class: "note even", style: "display:none"}
|
%tr{class: cycle('even', 'odd', name: 'articles')}
|
||||||
%td{colspan: "6"}=h oa.article.note
|
%th{colspan: "5"}= heading_helper GroupOrder, :total
|
||||||
%tr{class: cycle('even', 'odd', name: 'articles')}
|
%th= number_to_currency(@group_order.total)
|
||||||
%th{colspan: "5"}= heading_helper GroupOrder, :price
|
%br/
|
||||||
%th= number_to_currency(@group_order.price)
|
= link_to_top
|
||||||
- if @group_order.transport
|
%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?
|
||||||
%tr{class: cycle('even', 'odd', name: 'articles')}
|
- else
|
||||||
%td{colspan: "5"}= heading_helper GroupOrder, :transport
|
- if @order.open?
|
||||||
%td= number_to_currency(@group_order.transport)
|
= t '.articles.not_ordered_msg'
|
||||||
%tr{class: cycle('even', 'odd', name: 'articles')}
|
= link_to t('.articles.order_now'), action: "order", id: @order
|
||||||
%th{colspan: "5"}= heading_helper GroupOrder, :total
|
- else
|
||||||
%th= number_to_currency(@group_order.total)
|
= t '.articles.order_closed_msg'
|
||||||
%br/
|
|
||||||
= link_to_top
|
|
||||||
- else
|
|
||||||
- if @order.open?
|
|
||||||
= t '.articles.not_ordered_msg'
|
|
||||||
= link_to t('.articles.order_now'), action: "order", id: @order
|
|
||||||
- else
|
|
||||||
= 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
|
||||||
#new_comment= render 'order_comments/form', order_comment: @order.comments.build(user: current_user)
|
#new_comment= render 'order_comments/form', order_comment: @order.comments.build(user: current_user)
|
||||||
= link_to_top
|
= link_to_top
|
|
@ -1 +0,0 @@
|
||||||
= raw t '.text', group: @group.name, supplier: @supplier , foodcoop: FoodsoftConfig[:name]
|
|
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
|
||||||
|
|
|
@ -3,7 +3,7 @@ class String
|
||||||
# remove comma from decimal inputs
|
# remove comma from decimal inputs
|
||||||
def self.delocalized_decimal(string)
|
def self.delocalized_decimal(string)
|
||||||
if !string.blank? and string.is_a?(String)
|
if !string.blank? and string.is_a?(String)
|
||||||
BigDecimal.new(string.sub(',', '.'))
|
BigDecimal(string.sub(',', '.'))
|
||||||
else
|
else
|
||||||
string
|
string
|
||||||
end
|
end
|
||||||
|
|
|
@ -90,17 +90,6 @@ de:
|
||||||
tolerance: Toleranz
|
tolerance: Toleranz
|
||||||
total_price: Summe
|
total_price: Summe
|
||||||
unit_price: Preis/Einheit
|
unit_price: Preis/Einheit
|
||||||
group_order_invoice:
|
|
||||||
name: Bestellgruppenrechnung
|
|
||||||
links:
|
|
||||||
delete: Rechnung löschen
|
|
||||||
download: Rechnung herunterladen
|
|
||||||
generate: Rechnung erzeugen
|
|
||||||
invoice_date: Datum der Bestellgruppenrechnung
|
|
||||||
generate_with_date: setzen & erzeugen
|
|
||||||
|
|
||||||
payment_method: Guthaben
|
|
||||||
tax_number_not_set: Steuernummer in den Einstellungen nicht gesetzt
|
|
||||||
invoice:
|
invoice:
|
||||||
amount: Betrag
|
amount: Betrag
|
||||||
attachment: Anhang
|
attachment: Anhang
|
||||||
|
@ -329,7 +318,6 @@ de:
|
||||||
emails_title: E-Mails versenden
|
emails_title: E-Mails versenden
|
||||||
tab_payment:
|
tab_payment:
|
||||||
schedule_title: Bestellschema
|
schedule_title: Bestellschema
|
||||||
group_order_invoices: Bestellgruppenrechnungen
|
|
||||||
tab_security:
|
tab_security:
|
||||||
default_roles_title: Zugriff auf
|
default_roles_title: Zugriff auf
|
||||||
default_roles_paragraph: Jedes Mitglied der Foodcoop hat automatisch Zugriff auf folgende Bereiche.
|
default_roles_paragraph: Jedes Mitglied der Foodcoop hat automatisch Zugriff auf folgende Bereiche.
|
||||||
|
@ -580,6 +568,7 @@ de:
|
||||||
options:
|
options:
|
||||||
convert_units: Derzeitige Einheiten beibehalten, berechne Mengeneinheit und Preis (wie Synchronisieren).
|
convert_units: Derzeitige Einheiten beibehalten, berechne Mengeneinheit und Preis (wie Synchronisieren).
|
||||||
outlist_absent: Artikel löschen, die nicht in der hochgeladenen Datei sind.
|
outlist_absent: Artikel löschen, die nicht in der hochgeladenen Datei sind.
|
||||||
|
update_category: Kategorien aus der Datei übernehmen und erstellen.
|
||||||
sample:
|
sample:
|
||||||
juices: Säfte
|
juices: Säfte
|
||||||
nuts: Nüsse
|
nuts: Nüsse
|
||||||
|
@ -614,10 +603,6 @@ de:
|
||||||
email_from: E-Mails werden so aussehen, als ob sie von dieser Adresse gesendet wurden. Kann leer gelassen werden, um die Kontaktadresse der Foodcoop zu benutzen.
|
email_from: E-Mails werden so aussehen, als ob sie von dieser Adresse gesendet wurden. Kann leer gelassen werden, um die Kontaktadresse der Foodcoop zu benutzen.
|
||||||
email_replyto: Setze diese Adresse, wenn Du Antworten auf Foodsoft E-Mails auf eine andere, als die oben angegebene Absenderadresse bekommen möchtest.
|
email_replyto: Setze diese Adresse, wenn Du Antworten auf Foodsoft E-Mails auf eine andere, als die oben angegebene Absenderadresse bekommen möchtest.
|
||||||
email_sender: E-Mails werden so aussehen, als ob sie von dieser Adresse versendet wurden. Um zu vermeiden, dass E-Mails dadurch als Spam eingeordnet werden, muss der Webserver möglicherweise im SPF Eintrag der Domain der E-Mail Adresse eingetragen werden.
|
email_sender: E-Mails werden so aussehen, als ob sie von dieser Adresse versendet wurden. Um zu vermeiden, dass E-Mails dadurch als Spam eingeordnet werden, muss der Webserver möglicherweise im SPF Eintrag der Domain der E-Mail Adresse eingetragen werden.
|
||||||
group_order_invoices:
|
|
||||||
use_automativ_go_invoices: Es werden auf die Bestellgruppen zugeschnittene Rechnungen für die jeweilige Bestellung beim Klicken auf "abrechnen" an alle Bestellgruppenmitglieder per Mail versendet.
|
|
||||||
payment_method: Zahlungsart wird auf der Bestellgruppenrechnung deklariert
|
|
||||||
vat_exempt: Eine Auflistung der Rechnungsartikel erfolgt ohne explizite Ausweisung der MwSt. und die Rechnung erhält den notwendigen Zusatz bzgl. der Kleinunternehmerregelung §19 (FoodCoop Marge ebenfalls nicht in Rechnung enthalten)
|
|
||||||
help_url: Link zur Dokumentationsseite
|
help_url: Link zur Dokumentationsseite
|
||||||
homepage: Webseite der Foodcoop
|
homepage: Webseite der Foodcoop
|
||||||
ignore_browser_locale: Ignoriere die Sprache des Computers des Anwenders, wenn der Anwender noch keine Sprache gewählt hat.
|
ignore_browser_locale: Ignoriere die Sprache des Computers des Anwenders, wenn der Anwender noch keine Sprache gewählt hat.
|
||||||
|
@ -660,7 +645,6 @@ de:
|
||||||
phone: Telefon
|
phone: Telefon
|
||||||
street: Straße
|
street: Straße
|
||||||
zip_code: Postleitzahl
|
zip_code: Postleitzahl
|
||||||
tax_number: Steuernummer
|
|
||||||
currency_space: Leerzeichen hinzufügen
|
currency_space: Leerzeichen hinzufügen
|
||||||
currency_unit: Währung
|
currency_unit: Währung
|
||||||
custom_css: Angepasstes CSS
|
custom_css: Angepasstes CSS
|
||||||
|
@ -675,10 +659,6 @@ de:
|
||||||
email_from: Absenderadresse
|
email_from: Absenderadresse
|
||||||
email_replyto: Antwortadresse
|
email_replyto: Antwortadresse
|
||||||
email_sender: Senderadresse
|
email_sender: Senderadresse
|
||||||
group_order_invoices:
|
|
||||||
use_automatic_invoices: Automatisch bei Abrechnung per Mail versenden
|
|
||||||
payment_method: Zahlungsart
|
|
||||||
vat_exempt: Diese Foodcoop ist MwSt. befreit
|
|
||||||
help_url: URL Dokumentation
|
help_url: URL Dokumentation
|
||||||
homepage: Webseite
|
homepage: Webseite
|
||||||
ignore_browser_locale: Browsersprache ignorieren
|
ignore_browser_locale: Browsersprache ignorieren
|
||||||
|
@ -764,46 +744,6 @@ de:
|
||||||
update:
|
update:
|
||||||
notice: Lieferung wurde aktualisiert.
|
notice: Lieferung wurde aktualisiert.
|
||||||
documents:
|
documents:
|
||||||
group_order_invoice_pdf:
|
|
||||||
filename: Rechnung%{number}
|
|
||||||
invoicer: Rechnungsteller*in
|
|
||||||
invoicee: Rechnungsempfänger*in
|
|
||||||
invoice_date: 'Rechnungsdatum: %{invoice_date}'
|
|
||||||
invoice_number: 'Rechnungsnummer: %{invoice_number}'
|
|
||||||
markup_included: zzgl. Foodcoop Marge auf brutto Preis %{marge}%
|
|
||||||
ordergroup:
|
|
||||||
contact_phone: 'Telefonnummer: %{contact_phone}'
|
|
||||||
contact_address: 'Adresse : %{contact_address}'
|
|
||||||
name: Bestellgruppe %{ordergroup}
|
|
||||||
payment_method: 'Zahlungsart: %{payment_method}'
|
|
||||||
sum_to_pay: Zu zahlen gesamt
|
|
||||||
sum_to_pay_net: Zu zahlen gesamt (netto)
|
|
||||||
sum_to_pay_gross: Zu zahlen gesamt (brutto)
|
|
||||||
small_business_regulation: Als Kleinunternehmer*in im Sinne von §19 Abs. 1 Umsatzsteuergesetz (UStG) wird keine Umsatzsteuer berechnet.
|
|
||||||
table_headline: 'Für die Bestellung fallen folgende Posten an:'
|
|
||||||
tax_excluded: exkl. MwSt.
|
|
||||||
tax_included: zzgl. Gesamtsumme MwSt. %{tax}%
|
|
||||||
tax_number: 'Steuernummer: %{number}'
|
|
||||||
title: Rechnung für die Bestellung bei %{supplier}
|
|
||||||
vat_exempt_rows:
|
|
||||||
- Name
|
|
||||||
- Anzahl
|
|
||||||
- Einzelpreis
|
|
||||||
- Artikel Gesamtpreis
|
|
||||||
no_price_markup_rows:
|
|
||||||
- Name
|
|
||||||
- Anzahl
|
|
||||||
- Einzelpreis (netto)
|
|
||||||
- Artikel Gesamtpreis (netto)
|
|
||||||
- MwSt.
|
|
||||||
- Artikel Gesamtpreis (brutto)
|
|
||||||
price_markup_rows:
|
|
||||||
- Name
|
|
||||||
- Anzahl
|
|
||||||
- Einzelpreis (netto)
|
|
||||||
- Artikel Gesamtpreis (netto)
|
|
||||||
- MwSt.
|
|
||||||
- Artikel Gesamtpreis (brutto) inkl. Foodcoopmarge %{marge}%
|
|
||||||
order_by_articles:
|
order_by_articles:
|
||||||
filename: Bestellung %{name}-%{date} - Artikelsortierung
|
filename: Bestellung %{name}-%{date} - Artikelsortierung
|
||||||
title: 'Artikelsortierung der Bestellung: %{name}, beendet am %{date}'
|
title: 'Artikelsortierung der Bestellung: %{name}, beendet am %{date}'
|
||||||
|
@ -827,7 +767,6 @@ de:
|
||||||
heading: Artikelübersicht (%{count})
|
heading: Artikelübersicht (%{count})
|
||||||
title: 'Sortiermatrix der Bestellung: %{name}, beendet am %{date}'
|
title: 'Sortiermatrix der Bestellung: %{name}, beendet am %{date}'
|
||||||
errors:
|
errors:
|
||||||
check_tax_number: Überprüft, ob die Steuernummer der Foodcoop richtig gesetzt ist
|
|
||||||
general: Ein Problem ist aufgetreten.
|
general: Ein Problem ist aufgetreten.
|
||||||
general_again: Ein Fehler ist aufgetreten. Bitte erneut versuchen.
|
general_again: Ein Fehler ist aufgetreten. Bitte erneut versuchen.
|
||||||
general_msg: 'Ein Fehler ist aufgetreten: %{msg}'
|
general_msg: 'Ein Fehler ist aufgetreten: %{msg}'
|
||||||
|
@ -851,8 +790,6 @@ de:
|
||||||
close:
|
close:
|
||||||
alert: 'Ein Fehler ist beim Abrechnen aufgetreten: %{message}'
|
alert: 'Ein Fehler ist beim Abrechnen aufgetreten: %{message}'
|
||||||
notice: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert.
|
notice: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert.
|
||||||
notice_mail: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert. Außerdem wurden automatisch Rechnungen an die Bestellgruppenmitglieder geschickt.
|
|
||||||
settings_not_set: Keine Emails mit Bestellgruppenrechnungen versendet. Bitte überprüfe die Einstellungen. Steuernummer gesetzt?
|
|
||||||
close_all_direct_with_invoice:
|
close_all_direct_with_invoice:
|
||||||
notice: 'Es wurden %{count} Bestellung abgerechnet.'
|
notice: 'Es wurden %{count} Bestellung abgerechnet.'
|
||||||
close_direct:
|
close_direct:
|
||||||
|
@ -917,7 +854,6 @@ de:
|
||||||
ended: beendet
|
ended: beendet
|
||||||
name: Lieferantin
|
name: Lieferantin
|
||||||
no_closed_orders: Derzeit gibt es keine beendeten Bestellungen.
|
no_closed_orders: Derzeit gibt es keine beendeten Bestellungen.
|
||||||
|
|
||||||
state: Status
|
state: Status
|
||||||
summary:
|
summary:
|
||||||
changed: Daten wurden verändert!
|
changed: Daten wurden verändert!
|
||||||
|
@ -1110,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
|
||||||
|
@ -1164,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.
|
||||||
|
@ -1330,15 +1281,6 @@ de:
|
||||||
feedback:
|
feedback:
|
||||||
header: "%{user} schrieb am %{date}:"
|
header: "%{user} schrieb am %{date}:"
|
||||||
subject: Feedback zur Foodsoft
|
subject: Feedback zur Foodsoft
|
||||||
group_order_invoice:
|
|
||||||
subject: Bestellgruppenrechnung für %{group} bei %{supplier}
|
|
||||||
text: |
|
|
||||||
Liebe Bestellgruppe %{group},
|
|
||||||
|
|
||||||
Die Sammelbestellung bei %{supplier} wurde soeben abgerechnet und für die jeweiligen Bestellgruppen Rechnungen angelegt.
|
|
||||||
Im Anhang befindet sich daher eure Rechnung.
|
|
||||||
|
|
||||||
Viele Grüße von %{foodcoop}
|
|
||||||
invite:
|
invite:
|
||||||
subject: Einladung in die Foodcoop
|
subject: Einladung in die Foodcoop
|
||||||
text: |
|
text: |
|
||||||
|
@ -1538,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:
|
||||||
|
@ -1571,7 +1516,6 @@ de:
|
||||||
orders_finished: Beendet
|
orders_finished: Beendet
|
||||||
orders_open: Laufend
|
orders_open: Laufend
|
||||||
orders_settled: Abgerechnet
|
orders_settled: Abgerechnet
|
||||||
not_closed: Bestellung noch nicht abgerechnet
|
|
||||||
title: Bestellungen verwalten
|
title: Bestellungen verwalten
|
||||||
model:
|
model:
|
||||||
close_direct_message: Die Bestellung wurde abgechlossen, ohne die Mitgliederkonten zu belasten.
|
close_direct_message: Die Bestellung wurde abgechlossen, ohne die Mitgliederkonten zu belasten.
|
||||||
|
|
|
@ -90,16 +90,6 @@ en:
|
||||||
tolerance: Tolerance
|
tolerance: Tolerance
|
||||||
total_price: Sum
|
total_price: Sum
|
||||||
unit_price: Price/Unit
|
unit_price: Price/Unit
|
||||||
group_order_invoice:
|
|
||||||
name: Group order invoice
|
|
||||||
links:
|
|
||||||
delete: delete invoice
|
|
||||||
download: download invoice
|
|
||||||
invoice_date: date of group order invoice
|
|
||||||
generate: generate invoice
|
|
||||||
generate_with_date: set & generate
|
|
||||||
payment_method: Credit
|
|
||||||
tax_number_not_set: Tax number not set in configs
|
|
||||||
invoice:
|
invoice:
|
||||||
amount: Amount
|
amount: Amount
|
||||||
attachment: Attachment
|
attachment: Attachment
|
||||||
|
@ -328,7 +318,6 @@ en:
|
||||||
emails_title: Sending email
|
emails_title: Sending email
|
||||||
tab_payment:
|
tab_payment:
|
||||||
schedule_title: Ordering schedule
|
schedule_title: Ordering schedule
|
||||||
group_order_invoices: Group order invoices
|
|
||||||
tab_security:
|
tab_security:
|
||||||
default_roles_title: Access to
|
default_roles_title: Access to
|
||||||
default_roles_paragraph: By default every member of the foodcoop has access to the following areas.
|
default_roles_paragraph: By default every member of the foodcoop has access to the following areas.
|
||||||
|
@ -580,6 +569,7 @@ en:
|
||||||
options:
|
options:
|
||||||
convert_units: Keep current units, recompute unit quantity and price (like synchronize).
|
convert_units: Keep current units, recompute unit quantity and price (like synchronize).
|
||||||
outlist_absent: Delete articles not in uploaded file.
|
outlist_absent: Delete articles not in uploaded file.
|
||||||
|
update_category: Create and replace categories from uploaded file.
|
||||||
sample:
|
sample:
|
||||||
juices: Juices
|
juices: Juices
|
||||||
nuts: Nuts
|
nuts: Nuts
|
||||||
|
@ -614,9 +604,6 @@ en:
|
||||||
email_from: Emails will appear to be from this email address. Leave empty to use the foodcoop's contact address.
|
email_from: Emails will appear to be from this email address. Leave empty to use the foodcoop's contact address.
|
||||||
email_replyto: Set this when you want to receive replies from emails sent by Foodsoft on a different address than the above.
|
email_replyto: Set this when you want to receive replies from emails sent by Foodsoft on a different address than the above.
|
||||||
email_sender: Emails will appear to be sent from this email address. To avoid emails sent being classified as spam, the webserver may need to be registered in the SPF record of the email address's domain.
|
email_sender: Emails will appear to be sent from this email address. To avoid emails sent being classified as spam, the webserver may need to be registered in the SPF record of the email address's domain.
|
||||||
use_automatic_invoices: A listing of the invoice items is made without explicit display of VAT and the invoice receives the necessary addition regarding the small business regulation §19 (applies to Germany)
|
|
||||||
payment_method: Payment type is declared on the order group invoice
|
|
||||||
vat_exempt: A listing of the invoice items is made without explicit display of VAT and the invoice contains the necessary addition regarding the German Kleinunternehmerregelung §19 UStG (attention! FoodCoop marge not included in nvoice).
|
|
||||||
help_url: Documentation website.
|
help_url: Documentation website.
|
||||||
homepage: Website of your foodcoop.
|
homepage: Website of your foodcoop.
|
||||||
ignore_browser_locale: Ignore the language of user's computer when the user has not chosen a language yet.
|
ignore_browser_locale: Ignore the language of user's computer when the user has not chosen a language yet.
|
||||||
|
@ -644,8 +631,6 @@ en:
|
||||||
tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order.
|
tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order.
|
||||||
distribution_strategy: How articles should be distributed after an order has been received.
|
distribution_strategy: How articles should be distributed after an order has been received.
|
||||||
use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering.
|
use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering.
|
||||||
use_automatic_invoices: When an order is settled, invoices for the individual order groups are automatically sent by mail
|
|
||||||
payment_method: Payment Method for group order invoices
|
|
||||||
use_boxfill: When enabled, near end of an order, members are only able to change their order when increases the total amount ordered. This helps to fill any remaining boxes. You still need to set a box-fill date for the orders.
|
use_boxfill: When enabled, near end of an order, members are only able to change their order when increases the total amount ordered. This helps to fill any remaining boxes. You still need to set a box-fill date for the orders.
|
||||||
use_iban: When enabled, supplier and user provide an additonal field for storing the international bank account number.
|
use_iban: When enabled, supplier and user provide an additonal field for storing the international bank account number.
|
||||||
use_nick: Show and use nicknames instead of real names. When enabling this, please check that each user has a nickname.
|
use_nick: Show and use nicknames instead of real names. When enabling this, please check that each user has a nickname.
|
||||||
|
@ -661,7 +646,6 @@ en:
|
||||||
phone: Phone
|
phone: Phone
|
||||||
street: Street
|
street: Street
|
||||||
zip_code: Postcode
|
zip_code: Postcode
|
||||||
tax_number: Tax number
|
|
||||||
currency_space: add space
|
currency_space: add space
|
||||||
currency_unit: Currency
|
currency_unit: Currency
|
||||||
custom_css: Custom CSS
|
custom_css: Custom CSS
|
||||||
|
@ -705,10 +689,6 @@ en:
|
||||||
first_order_first_serve: First distribute to those who ordered first
|
first_order_first_serve: First distribute to those who ordered first
|
||||||
no_automatic_distribution: No automatic distribution
|
no_automatic_distribution: No automatic distribution
|
||||||
use_apple_points: Apple points
|
use_apple_points: Apple points
|
||||||
group_order_invoices:
|
|
||||||
use_automatic_invoices: Send automatically via mail after oder settlement
|
|
||||||
payment_method: Payment method
|
|
||||||
vat_exempt: This foodcoopis VAT exempt
|
|
||||||
use_boxfill: Box-fill phase
|
use_boxfill: Box-fill phase
|
||||||
use_iban: Use IBAN
|
use_iban: Use IBAN
|
||||||
use_nick: Use nicknames
|
use_nick: Use nicknames
|
||||||
|
@ -766,46 +746,6 @@ en:
|
||||||
update:
|
update:
|
||||||
notice: Delivery was updated.
|
notice: Delivery was updated.
|
||||||
documents:
|
documents:
|
||||||
group_order_invoice_pdf:
|
|
||||||
ordergroup:
|
|
||||||
contact_phone: 'Phone: %{contact_phone}'
|
|
||||||
contact_address: 'Adress : %{contact_address}'
|
|
||||||
name: 'Ordergroup: %{ordergroup}'
|
|
||||||
filename: Invoice%{number}
|
|
||||||
invoicee: Invoicee
|
|
||||||
invoicer: Invoicer
|
|
||||||
invoice_date: 'Invoice date: %{invoice_date}'
|
|
||||||
invoice_number: 'Invoice number: %{invoice_number}'
|
|
||||||
markup_included: incl Foodcoop Marge on gross price %{marge}%
|
|
||||||
payment_method: 'Payment_method: %{payment_method}'
|
|
||||||
small_business_regulation: As a small entrepreneur in the sense of §19 para. 1 of the Umsatzsteuergesetz (UStG), no value added tax is charged.
|
|
||||||
sum_to_pay: Total sum
|
|
||||||
sum_to_pay_net: Total sum (net)
|
|
||||||
sum_to_pay_gross: Total sum (gross)
|
|
||||||
table_headline: 'The following items will be charged for the order:'
|
|
||||||
tax_excluded: excl. MwSt.
|
|
||||||
tax_included: incl. VAT %{tax}%
|
|
||||||
tax_number: 'Tax number: %{number}'
|
|
||||||
title: Invoice for order at %{supplier}
|
|
||||||
vat_exempt_rows:
|
|
||||||
- Name
|
|
||||||
- Quantity
|
|
||||||
- Unit price
|
|
||||||
- Total price
|
|
||||||
no_price_markup_rows:
|
|
||||||
- Name
|
|
||||||
- Quantity
|
|
||||||
- Unit price (net)
|
|
||||||
- Total price (net)
|
|
||||||
- VAT
|
|
||||||
- Total price (gross)
|
|
||||||
price_markup_rows:
|
|
||||||
- Name
|
|
||||||
- Quantity
|
|
||||||
- Unit price (net)
|
|
||||||
- Total price (net)
|
|
||||||
- VAT
|
|
||||||
- Total price (gross) incl. foodcoop margin
|
|
||||||
order_by_articles:
|
order_by_articles:
|
||||||
filename: Order %{name}-%{date} - by articles
|
filename: Order %{name}-%{date} - by articles
|
||||||
title: 'Order sorted by articles: %{name}, closed at %{date}'
|
title: 'Order sorted by articles: %{name}, closed at %{date}'
|
||||||
|
@ -829,7 +769,6 @@ en:
|
||||||
heading: Article overview (%{count})
|
heading: Article overview (%{count})
|
||||||
title: 'Order sorting matrix: %{name}, closed at %{date}'
|
title: 'Order sorting matrix: %{name}, closed at %{date}'
|
||||||
errors:
|
errors:
|
||||||
check_tax_number: Please check whether the foodcoop's tax number is set correctly.
|
|
||||||
general: A problem has occured.
|
general: A problem has occured.
|
||||||
general_again: A problem has occured. Please try again.
|
general_again: A problem has occured. Please try again.
|
||||||
general_msg: 'A problem has occured: %{msg}'
|
general_msg: 'A problem has occured: %{msg}'
|
||||||
|
@ -853,7 +792,6 @@ en:
|
||||||
close:
|
close:
|
||||||
alert: 'An error occured while accounting: %{message}'
|
alert: 'An error occured while accounting: %{message}'
|
||||||
notice: Order was settled succesfully, the balance of the account was updated.
|
notice: Order was settled succesfully, the balance of the account was updated.
|
||||||
settings_not_set: No emails with order group invoices sent. Please check the settings. Tax number set?
|
|
||||||
close_all_direct_with_invoice:
|
close_all_direct_with_invoice:
|
||||||
notice: '%{count} orders have been settled.'
|
notice: '%{count} orders have been settled.'
|
||||||
close_direct:
|
close_direct:
|
||||||
|
@ -1110,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
|
||||||
|
@ -1164,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.
|
||||||
|
@ -1332,15 +1285,6 @@ en:
|
||||||
feedback:
|
feedback:
|
||||||
header: "%{user} wrote at %{date}:"
|
header: "%{user} wrote at %{date}:"
|
||||||
subject: Feedback for Foodsoft
|
subject: Feedback for Foodsoft
|
||||||
group_order_invoice:
|
|
||||||
subject: Order group invoice for %{group} at %{supplier}
|
|
||||||
text: |
|
|
||||||
Dear order group %{group},
|
|
||||||
|
|
||||||
The collective order at %{supplier} has just been settled and invoices have been created for the respective order groups.
|
|
||||||
Attached you will find your invoice.
|
|
||||||
|
|
||||||
Best regards from %{foodcoop}
|
|
||||||
from_via_foodsoft: "%{name} via Foodsoft"
|
from_via_foodsoft: "%{name} via Foodsoft"
|
||||||
invite:
|
invite:
|
||||||
subject: Invitation to the Foodcoop
|
subject: Invitation to the Foodcoop
|
||||||
|
@ -1546,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:
|
||||||
|
@ -1580,7 +1527,6 @@ en:
|
||||||
orders_finished: Closed
|
orders_finished: Closed
|
||||||
orders_open: Open
|
orders_open: Open
|
||||||
orders_settled: Settled
|
orders_settled: Settled
|
||||||
not_closed: Order not yet settled
|
|
||||||
title: Manage orders
|
title: Manage orders
|
||||||
model:
|
model:
|
||||||
close_direct_message: Order settled without charging member accounts.
|
close_direct_message: Order settled without charging member accounts.
|
||||||
|
@ -1700,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
|
||||||
|
|
|
@ -515,6 +515,7 @@ es:
|
||||||
options:
|
options:
|
||||||
convert_units: Mantener unidades actuales, recomputar la cantidad y precio de unidades (como sincronizar).
|
convert_units: Mantener unidades actuales, recomputar la cantidad y precio de unidades (como sincronizar).
|
||||||
outlist_absent: Borrar artículos que no están en el archivo subido.
|
outlist_absent: Borrar artículos que no están en el archivo subido.
|
||||||
|
update_category: Toma las categorías del archivo subido.
|
||||||
sample:
|
sample:
|
||||||
juices: Jugos
|
juices: Jugos
|
||||||
nuts: Nueces
|
nuts: Nueces
|
||||||
|
@ -929,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
|
||||||
|
@ -1260,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:
|
||||||
|
|
|
@ -539,6 +539,7 @@ nl:
|
||||||
options:
|
options:
|
||||||
convert_units: Bestaande eenheden behouden, herbereken groothandelseenheid en prijs (net als synchronizeren).
|
convert_units: Bestaande eenheden behouden, herbereken groothandelseenheid en prijs (net als synchronizeren).
|
||||||
outlist_absent: Artikelen die niet in het bestand voorkomen, verwijderen.
|
outlist_absent: Artikelen die niet in het bestand voorkomen, verwijderen.
|
||||||
|
upload_category: Categorieën overnemen uit bestand.
|
||||||
sample:
|
sample:
|
||||||
juices: Sappen
|
juices: Sappen
|
||||||
nuts: Noten
|
nuts: Noten
|
||||||
|
@ -1017,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.
|
||||||
|
@ -1024,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
|
||||||
|
@ -1071,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.
|
||||||
|
@ -1439,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
|
||||||
|
@ -142,10 +143,6 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
post 'finance/group_order_invoice', to: 'group_order_invoices#create_multiple'
|
|
||||||
resources :group_order_invoices
|
|
||||||
|
|
||||||
resources :article_categories
|
resources :article_categories
|
||||||
|
|
||||||
########### Finance
|
########### Finance
|
||||||
|
@ -178,7 +175,6 @@ Rails.application.routes.draw do
|
||||||
get :unpaid, on: :collection
|
get :unpaid, on: :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
resources :links, controller: 'financial_links', only: [:create, :show] do
|
resources :links, controller: 'financial_links', only: [:create, :show] do
|
||||||
collection do
|
collection do
|
||||||
get :incomplete
|
get :incomplete
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
class CreateGroupOrderInvoices < ActiveRecord::Migration[5.2]
|
|
||||||
def change
|
|
||||||
create_table :group_order_invoices do |t|
|
|
||||||
t.integer :group_order_id
|
|
||||||
t.bigint :invoice_number, unique: true, limit: 8
|
|
||||||
t.date :invoice_date
|
|
||||||
t.string :payment_method
|
|
||||||
|
|
||||||
t.timestamps
|
|
||||||
end
|
|
||||||
add_index :group_order_invoices, :group_order_id, unique: true
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
class MigrateMessageBodyToActionText < ActiveRecord::Migration[7.0]
|
||||||
|
include ActionView::Helpers::TextHelper
|
||||||
|
def change
|
||||||
|
rename_column :messages, :body, :body_old
|
||||||
|
Message.all.each do |message|
|
||||||
|
message.update_attribute(:body, simple_format(message.body_old))
|
||||||
|
end
|
||||||
|
remove_column :messages, :body_old
|
||||||
|
end
|
||||||
|
end
|
13
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2023_02_09_105256) do
|
ActiveRecord::Schema[7.0].define(version: 2023_02_15_085312) do
|
||||||
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.text "body", size: :long
|
t.text "body", size: :long
|
||||||
|
@ -192,16 +192,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_02_09_105256) do
|
||||||
t.index ["order_article_id"], name: "index_group_order_articles_on_order_article_id"
|
t.index ["order_article_id"], name: "index_group_order_articles_on_order_article_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "group_order_invoices", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
|
||||||
t.integer "group_order_id"
|
|
||||||
t.bigint "invoice_number"
|
|
||||||
t.date "invoice_date"
|
|
||||||
t.string "payment_method"
|
|
||||||
t.datetime "created_at", precision: nil, null: false
|
|
||||||
t.datetime "updated_at", precision: nil, null: false
|
|
||||||
t.index ["group_order_id"], name: "index_group_order_invoices_on_group_order_id", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "group_orders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
create_table "group_orders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
||||||
t.integer "ordergroup_id"
|
t.integer "ordergroup_id"
|
||||||
t.integer "order_id", default: 0, null: false
|
t.integer "order_id", default: 0, null: false
|
||||||
|
@ -302,7 +292,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_02_09_105256) do
|
||||||
create_table "messages", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
create_table "messages", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
||||||
t.integer "sender_id"
|
t.integer "sender_id"
|
||||||
t.string "subject", null: false
|
t.string "subject", null: false
|
||||||
t.text "body"
|
|
||||||
t.boolean "private", default: false
|
t.boolean "private", default: false
|
||||||
t.datetime "created_at", precision: nil
|
t.datetime "created_at", precision: nil
|
||||||
t.integer "reply_to"
|
t.integer "reply_to"
|
||||||
|
|
|
@ -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
|
||||||
|
|
147
db/seeds/demo-seeds.rb
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
require_relative 'seed_helper.rb'
|
||||||
|
|
||||||
|
FinancialTransactionClass.create!(:id => 1, :name => 'Standard')
|
||||||
|
FinancialTransactionClass.create!(:id => 2, :name => 'Foodsoft')
|
||||||
|
FinancialTransactionType.create!(:id => 1, :name => "Foodcoop", :financial_transaction_class_id => 1)
|
||||||
|
|
||||||
|
alice = User.create!(:id => 1, :nick => "alice", :password => "secret", :first_name => "Alice", :last_name => "Administrator", :email => "admin@foo.test", :phone => "+4421486548", :created_on => 'Wed, 15 Jan 2014 16:15:33 UTC +00:00')
|
||||||
|
bob = User.create!(:id => 2, :nick => "bob", :password => "secret", :first_name => "Bob", :last_name => "Doe", :email => "bob@doe.test", :created_on => 'Sun, 19 Jan 2014 17:38:22 UTC +00:00')
|
||||||
|
|
||||||
|
|
||||||
|
Workgroup.create!(:id => 1, :name => "Administrators", :description => "System administrators.", :account_balance => 0.0, :created_on => 'Wed, 15 Jan 2014 16:15:33 UTC +00:00', :role_admin => true, :role_suppliers => true, :role_article_meta => true, :role_finance => true, :role_orders => true, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false)
|
||||||
|
Workgroup.create!(:id => 2, :name => "Finances", :account_balance => 0.0, :created_on => 'Sun, 19 Jan 2014 17:40:03 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => true, :role_orders => false, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false)
|
||||||
|
Ordergroup.create!(:id => 5, :name => "Alice WG", :account_balance => 0.90E2, :created_on => 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :stats => { :jobs_size => 0, :orders_sum => 1021.74 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => true)
|
||||||
|
Ordergroup.create!(:id => 8, :name => "Bob's Family", :account_balance => 0.90E2, :created_on => 'Wed, 09 Apr 2014 12:23:29 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "John Doe", :stats => { :jobs_size => 0, :orders_sum => 0 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false)
|
||||||
|
FinancialTransaction.create!(:ordergroup_id => 5, :amount => 0.90E2, :note => "Bank transfer", :user_id => 2, :created_on => 'Mon, 17 Feb 2014 16:19:34 UTC +00:00', :financial_transaction_type_id => 1)
|
||||||
|
FinancialTransaction.create!(:ordergroup_id => 8, :amount => 0.90E2, :note => "Bank transfer", :user_id => 2, :created_on => 'Mon, 17 Feb 2014 16:19:34 UTC +00:00', :financial_transaction_type_id => 1)
|
||||||
|
|
||||||
|
Membership.create!(:group_id => 1, :user_id => 1)
|
||||||
|
Membership.create!(:group_id => 5, :user_id => 1)
|
||||||
|
Membership.create!(:group_id => 2, :user_id => 2)
|
||||||
|
Membership.create!(:group_id => 8, :user_id => 2)
|
||||||
|
|
||||||
|
supplier_category = SupplierCategory.create!(:id => 1, :name => "Other", :financial_transaction_class_id => 1)
|
||||||
|
|
||||||
|
chocolate_supplier = Supplier.create!(
|
||||||
|
name: "Kollektiv CHOCK!",
|
||||||
|
address: "Grabower Straße 1\n12345 Berlin",
|
||||||
|
phone: "0123456789",
|
||||||
|
email: "info@bbakery.test",
|
||||||
|
supplier_category: supplier_category
|
||||||
|
)
|
||||||
|
|
||||||
|
nkn_supplier = Supplier.create!(
|
||||||
|
name: "Naturgut Süd",
|
||||||
|
address: "Somewhere in Hamburg, maybe St. Pauli?",
|
||||||
|
phone: "0123434789",
|
||||||
|
email: "foodsoft@local-it.org",
|
||||||
|
supplier_category: supplier_category
|
||||||
|
)
|
||||||
|
|
||||||
|
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!(
|
||||||
|
name: "Vollmilch-Schokolade",
|
||||||
|
supplier_id: chocolate_supplier.id,
|
||||||
|
article_category_id: chocolate_category.id,
|
||||||
|
manufacturer: "Grabower Süßwaren GmbH",
|
||||||
|
origin: "D", price: 3.0, tax: 7.0,
|
||||||
|
unit: "200g", unit_quantity: 5,
|
||||||
|
note: "bio, fairtrade, 40% Kakao, vegan",
|
||||||
|
availability: true, order_number: "1")
|
||||||
|
|
||||||
|
Article.create!(
|
||||||
|
name: "Weiße Schokolade",
|
||||||
|
supplier_id: chocolate_supplier.id,
|
||||||
|
article_category_id: chocolate_category.id,
|
||||||
|
manufacturer: "Grabower Süßwaren GmbH",
|
||||||
|
origin: "D", price: 3.49, tax: 7.0,
|
||||||
|
unit: "200g", unit_quantity: 5,
|
||||||
|
note: "bio, fairtrade, 40% Kakao, vegan",
|
||||||
|
availability: true, order_number: "2")
|
||||||
|
|
||||||
|
dark_chocolate = Article.create!(
|
||||||
|
name: "Dunkle Schokolade",
|
||||||
|
supplier_id: chocolate_supplier.id,
|
||||||
|
article_category_id: chocolate_category.id,
|
||||||
|
manufacturer: "Grabower Süßwaren GmbH",
|
||||||
|
origin: "D", price: 2.89, tax: 7.0,
|
||||||
|
unit: "200g", unit_quantity: 5,
|
||||||
|
note: "bio, fairtrade, 40% Kakao, vegan",
|
||||||
|
availability: true, order_number: "3")
|
||||||
|
|
||||||
|
Article.create!(
|
||||||
|
name: "Himbeer-Schokolade",
|
||||||
|
supplier_id: chocolate_supplier.id,
|
||||||
|
article_category_id: chocolate_category.id,
|
||||||
|
manufacturer: "Grabower Süßwaren GmbH",
|
||||||
|
origin: "D", price: 2.89, tax: 7.0,
|
||||||
|
unit: "170g", unit_quantity: 4,
|
||||||
|
note: "bio, fairtrade, 40% Kakao, vegan",
|
||||||
|
availability: true, order_number: "4")
|
||||||
|
|
||||||
|
previous_order = seed_order(supplier_id: chocolate_supplier.id, starts: 10.days.ago, ends: 7.days.ago)
|
||||||
|
|
||||||
|
GroupOrderArticle.create!(
|
||||||
|
group_order: GroupOrder.create!(order_id: previous_order.id, ordergroup_id: 8),
|
||||||
|
order_article: previous_order.order_articles.find_by(article_id: dark_chocolate.id),
|
||||||
|
quantity: 5, tolerance: 0)
|
||||||
|
|
||||||
|
previous_order.close!(alice)
|
||||||
|
|
||||||
|
seed_order(supplier_id: chocolate_supplier.id, starts: 0.days.ago, ends: 7.days.from_now)
|
||||||
|
|
||||||
|
|
||||||
|
apple = Article.create!(
|
||||||
|
name: "Äpfel Elstar",
|
||||||
|
supplier_id: nkn_supplier.id,
|
||||||
|
article_category_id: obst_category.id,
|
||||||
|
manufacturer: "Obsthof Bruno Brugger",
|
||||||
|
origin: "D", price: 3.49, tax: 7.0,
|
||||||
|
unit: "1kg", unit_quantity: 10,
|
||||||
|
note: "lecker, fruchtig, demeter",
|
||||||
|
availability: true, order_number: "5")
|
||||||
|
|
||||||
|
brokkoli = Article.create!(
|
||||||
|
name: "Brokkoli",
|
||||||
|
supplier_id: nkn_supplier.id,
|
||||||
|
article_category_id: obst_category.id,
|
||||||
|
manufacturer: "Fattoria degli Orsi",
|
||||||
|
origin: "IT", price: 2.89, tax: 7.0,
|
||||||
|
unit: "400g", unit_quantity: 6,
|
||||||
|
note: "gesund und lecker",
|
||||||
|
availability: true, order_number: "6")
|
||||||
|
|
||||||
|
tomatoes = Article.create!(
|
||||||
|
name: "Tomaten",
|
||||||
|
supplier_id: nkn_supplier.id,
|
||||||
|
article_category_id: obst_category.id,
|
||||||
|
manufacturer: "Terra di Puglia",
|
||||||
|
origin: "IT", price: 2.89, tax: 7.0,
|
||||||
|
unit: "500g", unit_quantity: 20,
|
||||||
|
note: "pomodori italiani, demeter",
|
||||||
|
availability: true, order_number: "7")
|
||||||
|
|
||||||
|
rice = Article.create!(
|
||||||
|
name: "Reis",
|
||||||
|
supplier_id: nkn_supplier.id,
|
||||||
|
article_category_id: reis_category.id,
|
||||||
|
manufacturer: "Finck",
|
||||||
|
origin: "D", price: 3.29, tax: 7.0,
|
||||||
|
unit: "3kg", unit_quantity: 10,
|
||||||
|
note: "Reis im Vorratssack, demeter",
|
||||||
|
availability: true, order_number: "8")
|
||||||
|
|
||||||
|
spaghetti = Article.create!(
|
||||||
|
name: "Spaghetti",
|
||||||
|
supplier_id: nkn_supplier.id,
|
||||||
|
article_category_id: nudeln_category.id,
|
||||||
|
manufacturer: "Pastificio Zanellini spa",
|
||||||
|
origin: "D", price: 2.89, tax: 7.0,
|
||||||
|
unit: "500g", unit_quantity: 4,
|
||||||
|
note: "100% italienisches Hartweizengrieß",
|
||||||
|
availability: true, order_number: "9")
|
||||||
|
|
7
demo_day_nks.bnn
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
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;;
|
||||||
|
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;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;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;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;;
|
|
@ -66,6 +66,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
<<: *env
|
<<: *env
|
||||||
FOODSOFT_SERVICE: app
|
FOODSOFT_SERVICE: app
|
||||||
|
RAILS_SERVE_STATIC_FILES: 'true'
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
interval: 15s
|
interval: 15s
|
||||||
|
|
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 |
|
@ -32,7 +32,7 @@ class CurrentOrders::ArticlesController < ApplicationController
|
||||||
else
|
else
|
||||||
@order_articles = OrderArticle.where(order_id: @current_orders.all.map(&:id))
|
@order_articles = OrderArticle.where(order_id: @current_orders.all.map(&:id))
|
||||||
end
|
end
|
||||||
@q = OrderArticle.search(params[:q])
|
@q = OrderArticle.ransack(params[:q])
|
||||||
@order_articles = @order_articles.ordered.merge(@q.result).includes(:article, :article_price)
|
@order_articles = @order_articles.ordered.merge(@q.result).includes(:article, :article_price)
|
||||||
@order_article = @order_articles.where(id: params[:id]).first
|
@order_article = @order_articles.where(id: params[:id]).first
|
||||||
end
|
end
|
||||||
|
|
|
@ -187,8 +187,8 @@ describe ArticlesController, type: :controller do
|
||||||
describe '#parse_upload' do
|
describe '#parse_upload' do
|
||||||
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/files/upload_test.csv'), original_filename: 'upload_test.csv') }
|
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/files/upload_test.csv'), original_filename: 'upload_test.csv') }
|
||||||
|
|
||||||
it 'updates particles from spreadsheet' do
|
it 'updates articles from spreadsheet' do
|
||||||
post_with_supplier :parse_upload, params: { articles: { file: file, outlist_absent: '1', convert_units: '1' } }
|
post_with_supplier :parse_upload, params: { articles: { file: file, outlist_absent: '1', convert_units: '1', type: 'foodsoft' } }
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
50
spec/controllers/finance/ordergroups_controller_spec.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Finance::OrdergroupsController do
|
||||||
|
let(:user) { create(:user, :role_finance, :role_orders, :ordergroup) }
|
||||||
|
let(:fin_trans_type1) { create(:financial_transaction_type) }
|
||||||
|
let(:fin_trans_type2) { create(:financial_transaction_type) }
|
||||||
|
let(:fin_trans1) do
|
||||||
|
create(:financial_transaction,
|
||||||
|
user: user,
|
||||||
|
ordergroup: user.ordergroup,
|
||||||
|
financial_transaction_type: fin_trans_type1)
|
||||||
|
end
|
||||||
|
let(:fin_trans2) do
|
||||||
|
create(:financial_transaction,
|
||||||
|
user: user,
|
||||||
|
ordergroup: user.ordergroup,
|
||||||
|
financial_transaction_type: fin_trans_type1)
|
||||||
|
end
|
||||||
|
let(:fin_trans3) do
|
||||||
|
create(:financial_transaction,
|
||||||
|
user: user,
|
||||||
|
ordergroup: user.ordergroup,
|
||||||
|
financial_transaction_type: fin_trans_type2)
|
||||||
|
end
|
||||||
|
|
||||||
|
before { login user }
|
||||||
|
|
||||||
|
describe 'GET index' do
|
||||||
|
before do
|
||||||
|
fin_trans1
|
||||||
|
fin_trans2
|
||||||
|
fin_trans3
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders index page' do
|
||||||
|
get_with_defaults :index
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response).to render_template('finance/ordergroups/index')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calculates total balance sums correctly' do
|
||||||
|
get_with_defaults :index
|
||||||
|
expect(assigns(:total_balances).size).to eq(2)
|
||||||
|
expect(assigns(:total_balances)[fin_trans_type1.id]).to eq(fin_trans1.amount + fin_trans2.amount)
|
||||||
|
expect(assigns(:total_balances)[fin_trans_type2.id]).to eq(fin_trans3.amount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
require 'factory_bot'
|
|
||||||
|
|
||||||
FactoryBot.define do
|
|
||||||
factory :group_order_invoice do
|
|
||||||
group_order { create :group_order }
|
|
||||||
end
|
|
||||||
end
|
|
5
spec/fixtures/bnn_file_01.bnn
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1
|
||||||
|
29932;;;;4280001958081;4280001958203;Walnoten (ongeroosterd);bio;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;1 kg;1;N;930190;99260;;1,41;;;;1;;;4,49;2,34;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;;
|
||||||
|
28391;;;;4280001958081;4280001958203;Pijnboompitten;dem;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;100 g;10;N;930190;99260;;1,41;;;;1;;;5,56;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;;
|
||||||
|
1829;;;;4280001958081;4280001958203;Appelsap (verpakt);;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;4x250 ml;10;4x250 ml;10;N;930190;99260;;3,21;;;;1;;;4,49;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;ml;28,571;;
|
||||||
|
177813;;;;4280001958081;4280001958203;Tomaten;bio;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;500 g;20;N;930190;99260;;1,20;;;;1;;;4,49;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;g;28,571;;
|
2
spec/fixtures/bnn_file_02.bnn
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1
|
||||||
|
1;;;;4280001958081;4280001958203;Tomatoes;organic;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;20;500 g;1;N;930190;99260;;1,41;;;;1;;;4,49;1,20;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;g;28,571;;
|
273
spec/fixtures/odin_file_01.xml
vendored
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<xmlproduct>
|
||||||
|
<leverancierkop>
|
||||||
|
<leveranciersnummer>1039</leveranciersnummer>
|
||||||
|
<versienummer>1.08</versienummer>
|
||||||
|
<naam>Estafette Associatie C.V.</naam>
|
||||||
|
<plaats>Geldermalsen</plaats>
|
||||||
|
</leverancierkop>
|
||||||
|
<product>
|
||||||
|
<eancode>8719325207668</eancode>
|
||||||
|
<omschrijving>Walnoten (ongeroosterd)</omschrijving>
|
||||||
|
<kassaomschrijving>Nucli rose</kassaomschrijving>
|
||||||
|
<plucode></plucode>
|
||||||
|
<weegschaalartikel>0</weegschaalartikel>
|
||||||
|
<wichtartikel>0</wichtartikel>
|
||||||
|
<pluartikel>0</pluartikel>
|
||||||
|
<inhoud>1</inhoud>
|
||||||
|
<eenheid>kg</eenheid>
|
||||||
|
<verpakkingce>Stuk</verpakkingce>
|
||||||
|
<statiegeld>0</statiegeld>
|
||||||
|
<merk>Het warme woud</merk>
|
||||||
|
<kwaliteit>bio</kwaliteit>
|
||||||
|
<keurmerkbio></keurmerkbio>
|
||||||
|
<keurmerkoverig></keurmerkoverig>
|
||||||
|
<herkomst>NL</herkomst>
|
||||||
|
<herkomstregio></herkomstregio>
|
||||||
|
<btw>6</btw>
|
||||||
|
<cblcode>1017515</cblcode>
|
||||||
|
<bestelnummer>29932</bestelnummer>
|
||||||
|
<sve>10</sve>
|
||||||
|
<status>Actief</status>
|
||||||
|
<ingredienten>druiven*</ingredienten>
|
||||||
|
<d204>0</d204>
|
||||||
|
<d209>0</d209>
|
||||||
|
<d210>2</d210>
|
||||||
|
<d212>2</d212>
|
||||||
|
<d213>0</d213>
|
||||||
|
<d214>0</d214>
|
||||||
|
<d234>0</d234>
|
||||||
|
<d215>2</d215>
|
||||||
|
<d239>2</d239>
|
||||||
|
<d216>0</d216>
|
||||||
|
<d217>2</d217>
|
||||||
|
<d220>0</d220>
|
||||||
|
<d221>2</d221>
|
||||||
|
<d223>0</d223>
|
||||||
|
<d236>2</d236>
|
||||||
|
<d235>2</d235>
|
||||||
|
<d238>2</d238>
|
||||||
|
<d225>2</d225>
|
||||||
|
<d228>1</d228>
|
||||||
|
<d232>0</d232>
|
||||||
|
<d237>2</d237>
|
||||||
|
<d240>0</d240>
|
||||||
|
<d241>2</d241>
|
||||||
|
<d242>2</d242>
|
||||||
|
<lengteverpakking></lengteverpakking>
|
||||||
|
<breedteverpakking></breedteverpakking>
|
||||||
|
<hoogteverpakking></hoogteverpakking>
|
||||||
|
<proefdiervrij>0</proefdiervrij>
|
||||||
|
<vegetarisch>0</vegetarisch>
|
||||||
|
<veganistisch>0</veganistisch>
|
||||||
|
<rauwemelk>0</rauwemelk>
|
||||||
|
<bewaartemperatuur>1</bewaartemperatuur>
|
||||||
|
<gebruikstips></gebruikstips>
|
||||||
|
<soortleverancier>2</soortleverancier>
|
||||||
|
<geriefartikel>0</geriefartikel>
|
||||||
|
<prijs>
|
||||||
|
<prijslijn>adviesprijs</prijslijn>
|
||||||
|
<ingangsdatum>2022-08-18</ingangsdatum>
|
||||||
|
<inkoopprijs>2.34</inkoopprijs>
|
||||||
|
<consumentenprijs>7.95</consumentenprijs>
|
||||||
|
</prijs>
|
||||||
|
</product>
|
||||||
|
<product>
|
||||||
|
<eancode>8719325207668</eancode>
|
||||||
|
<omschrijving>Pijnboompitten</omschrijving>
|
||||||
|
<kassaomschrijving>Nucli rose</kassaomschrijving>
|
||||||
|
<plucode></plucode>
|
||||||
|
<weegschaalartikel>0</weegschaalartikel>
|
||||||
|
<wichtartikel>0</wichtartikel>
|
||||||
|
<pluartikel>0</pluartikel>
|
||||||
|
<inhoud>100</inhoud>
|
||||||
|
<eenheid>g</eenheid>
|
||||||
|
<verpakkingce>Stuk</verpakkingce>
|
||||||
|
<statiegeld>0</statiegeld>
|
||||||
|
<merk>NELEMAN</merk>
|
||||||
|
<kwaliteit>dem</kwaliteit>
|
||||||
|
<keurmerkbio></keurmerkbio>
|
||||||
|
<keurmerkoverig></keurmerkoverig>
|
||||||
|
<herkomst>TR</herkomst>
|
||||||
|
<herkomstregio></herkomstregio>
|
||||||
|
<btw>6</btw>
|
||||||
|
<cblcode>1017515</cblcode>
|
||||||
|
<bestelnummer>28391</bestelnummer>
|
||||||
|
<sve>10</sve>
|
||||||
|
<status>Actief</status>
|
||||||
|
<ingredienten>druiven*</ingredienten>
|
||||||
|
<d204>0</d204>
|
||||||
|
<d209>0</d209>
|
||||||
|
<d210>2</d210>
|
||||||
|
<d212>2</d212>
|
||||||
|
<d213>0</d213>
|
||||||
|
<d214>0</d214>
|
||||||
|
<d234>0</d234>
|
||||||
|
<d215>2</d215>
|
||||||
|
<d239>2</d239>
|
||||||
|
<d216>0</d216>
|
||||||
|
<d217>2</d217>
|
||||||
|
<d220>0</d220>
|
||||||
|
<d221>2</d221>
|
||||||
|
<d223>0</d223>
|
||||||
|
<d236>2</d236>
|
||||||
|
<d235>2</d235>
|
||||||
|
<d238>2</d238>
|
||||||
|
<d225>2</d225>
|
||||||
|
<d228>1</d228>
|
||||||
|
<d232>0</d232>
|
||||||
|
<d237>2</d237>
|
||||||
|
<d240>0</d240>
|
||||||
|
<d241>2</d241>
|
||||||
|
<d242>2</d242>
|
||||||
|
<lengteverpakking></lengteverpakking>
|
||||||
|
<breedteverpakking></breedteverpakking>
|
||||||
|
<hoogteverpakking></hoogteverpakking>
|
||||||
|
<proefdiervrij>0</proefdiervrij>
|
||||||
|
<vegetarisch>0</vegetarisch>
|
||||||
|
<veganistisch>0</veganistisch>
|
||||||
|
<rauwemelk>0</rauwemelk>
|
||||||
|
<bewaartemperatuur>1</bewaartemperatuur>
|
||||||
|
<gebruikstips></gebruikstips>
|
||||||
|
<soortleverancier>2</soortleverancier>
|
||||||
|
<geriefartikel>0</geriefartikel>
|
||||||
|
<prijs>
|
||||||
|
<prijslijn>adviesprijs</prijslijn>
|
||||||
|
<ingangsdatum>2022-08-18</ingangsdatum>
|
||||||
|
<inkoopprijs>5.56</inkoopprijs>
|
||||||
|
<consumentenprijs>7.95</consumentenprijs>
|
||||||
|
</prijs>
|
||||||
|
</product>
|
||||||
|
<product>
|
||||||
|
<eancode>8719325207668</eancode>
|
||||||
|
<omschrijving>Appelsap (verpakt)</omschrijving>
|
||||||
|
<kassaomschrijving>Nucli rose</kassaomschrijving>
|
||||||
|
<plucode></plucode>
|
||||||
|
<weegschaalartikel>0</weegschaalartikel>
|
||||||
|
<wichtartikel>0</wichtartikel>
|
||||||
|
<pluartikel>0</pluartikel>
|
||||||
|
<inhoud>4x250</inhoud>
|
||||||
|
<eenheid>ml</eenheid>
|
||||||
|
<verpakkingce>Stuk</verpakkingce>
|
||||||
|
<statiegeld>0.4</statiegeld>
|
||||||
|
<merk>Appelgaarde</merk>
|
||||||
|
<kwaliteit></kwaliteit>
|
||||||
|
<keurmerkbio></keurmerkbio>
|
||||||
|
<keurmerkoverig></keurmerkoverig>
|
||||||
|
<herkomst>DE</herkomst>
|
||||||
|
<herkomstregio></herkomstregio>
|
||||||
|
<btw>6</btw>
|
||||||
|
<cblcode>1017515</cblcode>
|
||||||
|
<bestelnummer>1829</bestelnummer>
|
||||||
|
<sve>10</sve>
|
||||||
|
<status>Actief</status>
|
||||||
|
<ingredienten>druiven*</ingredienten>
|
||||||
|
<d204>0</d204>
|
||||||
|
<d209>0</d209>
|
||||||
|
<d210>2</d210>
|
||||||
|
<d212>2</d212>
|
||||||
|
<d213>0</d213>
|
||||||
|
<d214>0</d214>
|
||||||
|
<d234>0</d234>
|
||||||
|
<d215>2</d215>
|
||||||
|
<d239>2</d239>
|
||||||
|
<d216>0</d216>
|
||||||
|
<d217>2</d217>
|
||||||
|
<d220>0</d220>
|
||||||
|
<d221>2</d221>
|
||||||
|
<d223>0</d223>
|
||||||
|
<d236>2</d236>
|
||||||
|
<d235>2</d235>
|
||||||
|
<d238>2</d238>
|
||||||
|
<d225>2</d225>
|
||||||
|
<d228>1</d228>
|
||||||
|
<d232>0</d232>
|
||||||
|
<d237>2</d237>
|
||||||
|
<d240>0</d240>
|
||||||
|
<d241>2</d241>
|
||||||
|
<d242>2</d242>
|
||||||
|
<lengteverpakking></lengteverpakking>
|
||||||
|
<breedteverpakking></breedteverpakking>
|
||||||
|
<hoogteverpakking></hoogteverpakking>
|
||||||
|
<proefdiervrij>0</proefdiervrij>
|
||||||
|
<vegetarisch>0</vegetarisch>
|
||||||
|
<veganistisch>0</veganistisch>
|
||||||
|
<rauwemelk>0</rauwemelk>
|
||||||
|
<bewaartemperatuur>1</bewaartemperatuur>
|
||||||
|
<gebruikstips></gebruikstips>
|
||||||
|
<soortleverancier>2</soortleverancier>
|
||||||
|
<geriefartikel>0</geriefartikel>
|
||||||
|
<prijs>
|
||||||
|
<prijslijn>adviesprijs</prijslijn>
|
||||||
|
<ingangsdatum>2022-08-18</ingangsdatum>
|
||||||
|
<inkoopprijs>3.21</inkoopprijs>
|
||||||
|
<consumentenprijs>7.95</consumentenprijs>
|
||||||
|
</prijs>
|
||||||
|
</product>
|
||||||
|
<product>
|
||||||
|
<eancode>8719325207668</eancode>
|
||||||
|
<omschrijving>Tomaten</omschrijving>
|
||||||
|
<kassaomschrijving>Nucli rose</kassaomschrijving>
|
||||||
|
<plucode></plucode>
|
||||||
|
<weegschaalartikel>0</weegschaalartikel>
|
||||||
|
<wichtartikel>0</wichtartikel>
|
||||||
|
<pluartikel>0</pluartikel>
|
||||||
|
<inhoud>500</inhoud>
|
||||||
|
<eenheid>g</eenheid>
|
||||||
|
<verpakkingce>Stuk</verpakkingce>
|
||||||
|
<statiegeld>0</statiegeld>
|
||||||
|
<merk>De röde hof</merk>
|
||||||
|
<kwaliteit>bio</kwaliteit>
|
||||||
|
<keurmerkbio></keurmerkbio>
|
||||||
|
<keurmerkoverig></keurmerkoverig>
|
||||||
|
<herkomst>DE</herkomst>
|
||||||
|
<herkomstregio></herkomstregio>
|
||||||
|
<btw>6</btw>
|
||||||
|
<cblcode>1017515</cblcode>
|
||||||
|
<bestelnummer>177813</bestelnummer>
|
||||||
|
<sve>20</sve>
|
||||||
|
<status>Actief</status>
|
||||||
|
<ingredienten>druiven*</ingredienten>
|
||||||
|
<d204>0</d204>
|
||||||
|
<d209>0</d209>
|
||||||
|
<d210>2</d210>
|
||||||
|
<d212>2</d212>
|
||||||
|
<d213>0</d213>
|
||||||
|
<d214>0</d214>
|
||||||
|
<d234>0</d234>
|
||||||
|
<d215>2</d215>
|
||||||
|
<d239>2</d239>
|
||||||
|
<d216>0</d216>
|
||||||
|
<d217>2</d217>
|
||||||
|
<d220>0</d220>
|
||||||
|
<d221>2</d221>
|
||||||
|
<d223>0</d223>
|
||||||
|
<d236>2</d236>
|
||||||
|
<d235>2</d235>
|
||||||
|
<d238>2</d238>
|
||||||
|
<d225>2</d225>
|
||||||
|
<d228>1</d228>
|
||||||
|
<d232>0</d232>
|
||||||
|
<d237>2</d237>
|
||||||
|
<d240>0</d240>
|
||||||
|
<d241>2</d241>
|
||||||
|
<d242>2</d242>
|
||||||
|
<lengteverpakking></lengteverpakking>
|
||||||
|
<breedteverpakking></breedteverpakking>
|
||||||
|
<hoogteverpakking></hoogteverpakking>
|
||||||
|
<proefdiervrij>0</proefdiervrij>
|
||||||
|
<vegetarisch>0</vegetarisch>
|
||||||
|
<veganistisch>0</veganistisch>
|
||||||
|
<rauwemelk>0</rauwemelk>
|
||||||
|
<bewaartemperatuur>1</bewaartemperatuur>
|
||||||
|
<gebruikstips></gebruikstips>
|
||||||
|
<soortleverancier>2</soortleverancier>
|
||||||
|
<geriefartikel>0</geriefartikel>
|
||||||
|
<prijs>
|
||||||
|
<prijslijn>adviesprijs</prijslijn>
|
||||||
|
<ingangsdatum>2022-08-18</ingangsdatum>
|
||||||
|
<inkoopprijs>1.2</inkoopprijs>
|
||||||
|
<consumentenprijs>7.95</consumentenprijs>
|
||||||
|
</prijs>
|
||||||
|
</product>
|
||||||
|
</xmlproduct>
|
75
spec/fixtures/odin_file_02.xml
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<xmlproduct>
|
||||||
|
<leverancierkop>
|
||||||
|
<leveranciersnummer>1039</leveranciersnummer>
|
||||||
|
<versienummer>1.08</versienummer>
|
||||||
|
<naam>Estafette Associatie C.V.</naam>
|
||||||
|
<plaats>Geldermalsen</plaats>
|
||||||
|
</leverancierkop>
|
||||||
|
<product>
|
||||||
|
<eancode>8719325207668</eancode>
|
||||||
|
<omschrijving>Tomatoes</omschrijving>
|
||||||
|
<kassaomschrijving>Nucli rose</kassaomschrijving>
|
||||||
|
<plucode></plucode>
|
||||||
|
<weegschaalartikel>0</weegschaalartikel>
|
||||||
|
<wichtartikel>0</wichtartikel>
|
||||||
|
<pluartikel>0</pluartikel>
|
||||||
|
<inhoud>500</inhoud>
|
||||||
|
<eenheid>g</eenheid>
|
||||||
|
<verpakkingce>Stuk</verpakkingce>
|
||||||
|
<statiegeld>0</statiegeld>
|
||||||
|
<merk>De röde hof</merk>
|
||||||
|
<kwaliteit>organic</kwaliteit>
|
||||||
|
<keurmerkbio></keurmerkbio>
|
||||||
|
<keurmerkoverig></keurmerkoverig>
|
||||||
|
<herkomst>Somewhere, UK</herkomst>
|
||||||
|
<herkomstregio></herkomstregio>
|
||||||
|
<btw>6</btw>
|
||||||
|
<cblcode>1017515</cblcode>
|
||||||
|
<bestelnummer>1</bestelnummer>
|
||||||
|
<sve>20</sve>
|
||||||
|
<status>Actief</status>
|
||||||
|
<ingredienten>druiven*</ingredienten>
|
||||||
|
<d204>0</d204>
|
||||||
|
<d209>0</d209>
|
||||||
|
<d210>2</d210>
|
||||||
|
<d212>2</d212>
|
||||||
|
<d213>0</d213>
|
||||||
|
<d214>0</d214>
|
||||||
|
<d234>0</d234>
|
||||||
|
<d215>2</d215>
|
||||||
|
<d239>2</d239>
|
||||||
|
<d216>0</d216>
|
||||||
|
<d217>2</d217>
|
||||||
|
<d220>0</d220>
|
||||||
|
<d221>2</d221>
|
||||||
|
<d223>0</d223>
|
||||||
|
<d236>2</d236>
|
||||||
|
<d235>2</d235>
|
||||||
|
<d238>2</d238>
|
||||||
|
<d225>2</d225>
|
||||||
|
<d228>1</d228>
|
||||||
|
<d232>0</d232>
|
||||||
|
<d237>2</d237>
|
||||||
|
<d240>0</d240>
|
||||||
|
<d241>2</d241>
|
||||||
|
<d242>2</d242>
|
||||||
|
<lengteverpakking></lengteverpakking>
|
||||||
|
<breedteverpakking></breedteverpakking>
|
||||||
|
<hoogteverpakking></hoogteverpakking>
|
||||||
|
<proefdiervrij>0</proefdiervrij>
|
||||||
|
<vegetarisch>0</vegetarisch>
|
||||||
|
<veganistisch>0</veganistisch>
|
||||||
|
<rauwemelk>0</rauwemelk>
|
||||||
|
<bewaartemperatuur>1</bewaartemperatuur>
|
||||||
|
<gebruikstips></gebruikstips>
|
||||||
|
<soortleverancier>2</soortleverancier>
|
||||||
|
<geriefartikel>0</geriefartikel>
|
||||||
|
<prijs>
|
||||||
|
<prijslijn>adviesprijs</prijslijn>
|
||||||
|
<ingangsdatum>2022-08-18</ingangsdatum>
|
||||||
|
<inkoopprijs>1.2</inkoopprijs>
|
||||||
|
<consumentenprijs>7.95</consumentenprijs>
|
||||||
|
</prijs>
|
||||||
|
</product>
|
||||||
|
</xmlproduct>
|
|
@ -1,14 +1,17 @@
|
||||||
require_relative '../spec_helper'
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
feature ArticlesController do
|
feature ArticlesController do
|
||||||
let(:user) { create :user, groups: [create(:workgroup, role_article_meta: true)] }
|
let(:user) { create(:user, groups: [create(:workgroup, role_article_meta: true)]) }
|
||||||
let(:supplier) { create :supplier }
|
let(:supplier) { create(:supplier) }
|
||||||
let!(:article_category) { create :article_category }
|
let!(:article_category) { create(:article_category) }
|
||||||
|
|
||||||
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)
|
||||||
|
@ -18,7 +21,7 @@ feature ArticlesController do
|
||||||
it 'can create a new article' do
|
it 'can create a new article' do
|
||||||
click_on I18n.t('articles.index.new')
|
click_on I18n.t('articles.index.new')
|
||||||
expect(page).to have_selector('form#new_article')
|
expect(page).to have_selector('form#new_article')
|
||||||
article = build :article, supplier: supplier, article_category: article_category
|
article = build(:article, supplier: supplier, article_category: article_category)
|
||||||
within('#new_article') do
|
within('#new_article') do
|
||||||
fill_in 'article_name', :with => article.name
|
fill_in 'article_name', :with => article.name
|
||||||
fill_in 'article_unit', :with => article.unit
|
fill_in 'article_unit', :with => article.unit
|
||||||
|
@ -49,6 +52,7 @@ feature ArticlesController do
|
||||||
let(:file) { Rails.root.join(test_file) }
|
let(:file) { Rails.root.join(test_file) }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
find("#articles_type option[value='foodsoft']").select_option
|
||||||
find('input[type="submit"]').click
|
find('input[type="submit"]').click
|
||||||
expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio ◎"
|
expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio ◎"
|
||||||
expect(find("tr:nth-child(2) #new_articles__name").value).to eq "Pijnboompitten"
|
expect(find("tr:nth-child(2) #new_articles__name").value).to eq "Pijnboompitten"
|
||||||
|
@ -64,56 +68,124 @@ feature ArticlesController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "can update existing article" do
|
Dir.glob('spec/fixtures/bnn_file_01.*') do |test_file|
|
||||||
let!(:article) { create :article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g' }
|
describe "can import articles from #{test_file}" do
|
||||||
|
let(:file) { Rails.root.join(test_file) }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
find('input[type="submit"]').click
|
find("#articles_type option[value='bnn']").select_option
|
||||||
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
|
find('input[type="submit"]').click
|
||||||
find('input[type="submit"]').click
|
expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio"
|
||||||
article.reload
|
expect(find("tr:nth-child(1) #new_articles__name").value).to eq "Walnoten (ongeroosterd)"
|
||||||
expect(article.name).to eq 'Tomatoes'
|
# set article category
|
||||||
expect([article.unit, article.unit_quantity, article.price]).to eq ['500 g', 20, 1.2]
|
4.times do |i|
|
||||||
|
all("tr:nth-child(#{i + 1}) select > option")[1].select_option
|
||||||
|
end
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
|
||||||
|
expect(page).to have_content("Pijnboompitten")
|
||||||
|
|
||||||
|
expect(supplier.articles.count).to eq 4
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "handles missing data" do
|
describe "updates" do
|
||||||
it do
|
file_paths = ['spec/fixtures/foodsoft_file_02.csv', 'spec/fixtures/bnn_file_02.bnn', 'spec/fixtures/odin_file_02.xml']
|
||||||
find('input[type="submit"]').click # to overview
|
let(:filename) { 'foodsoft_file_02.csv' }
|
||||||
find('input[type="submit"]').click # missing category, re-show form
|
let(:file) { Rails.root.join("spec/fixtures/#{filename}") }
|
||||||
expect(find('tr.alert')).to be_present
|
let(:val) { 'foodsoft' }
|
||||||
expect(supplier.articles.count).to eq 0
|
let(:type) { %w[foodsoft bnn odin] }
|
||||||
|
|
||||||
all("tr select > option")[1].select_option
|
before do
|
||||||
find('input[type="submit"]').click # now it should succeed
|
visit upload_supplier_articles_path(supplier_id: supplier.id)
|
||||||
expect(supplier.articles.count).to eq 1
|
attach_file 'articles_file', file
|
||||||
end
|
find("#articles_type option[value='#{val}']").select_option
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "can remove an existing article" do
|
file_paths.each_with_index do |test_file, index|
|
||||||
let!(:article) { create :article, supplier: supplier, name: 'Foobar', order_number: 99999 }
|
describe "updates article for #{test_file}" do
|
||||||
|
let(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g') }
|
||||||
|
let(:file) { Rails.root.join(test_file) }
|
||||||
|
let(:val) { type[index] }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
check('articles_outlist_absent')
|
article.reload
|
||||||
find('input[type="submit"]').click
|
find('input[type="submit"]').click
|
||||||
expect(find("#outlisted_articles_#{article.id}", visible: :all)).to be_present
|
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
article.reload
|
||||||
|
expect(article.name).to eq 'Tomatoes'
|
||||||
|
if type[index] == "odin"
|
||||||
|
expect([article.unit, article.unit_quantity, article.price]).to eq ['500gr', 20, 1.20]
|
||||||
|
else
|
||||||
|
expect([article.unit, article.unit_quantity, article.price]).to eq ['500 g', 20, 1.20]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
all("tr select > option")[1].select_option
|
it "handles missing data" do
|
||||||
find('input[type="submit"]').click
|
find('input[type="submit"]').click # to overview
|
||||||
expect(article.reload.deleted?).to be true
|
find('input[type="submit"]').click # missing category, re-show form
|
||||||
|
expect(find('tr.alert')).to be_present
|
||||||
|
expect(supplier.articles.count).to eq 0
|
||||||
|
|
||||||
|
all("tr select > option")[1].select_option
|
||||||
|
find('input[type="submit"]').click # now it should succeed
|
||||||
|
expect(supplier.articles.count).to eq 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe "can convert units when updating" do
|
describe "takes over category from file" do
|
||||||
let!(:article) { create :article, supplier: supplier, order_number: 1, unit: '250 g' }
|
it do
|
||||||
|
find(:css, '#articles_update_category[value="1"]').set(true) # check take over category from file
|
||||||
|
expect(ArticleCategory.count).to eq 1 # new Category vegetables should be created from file
|
||||||
|
find('input[type="submit"]').click # upload file
|
||||||
|
find('input[type="submit"]').click # submit changes
|
||||||
|
expect(ArticleCategory.count).to eq 2 # it is
|
||||||
|
expect(supplier.articles.count).to eq 1
|
||||||
|
expect(supplier.articles.first.article_category.name).to eq "Vegetables"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it do
|
describe "overwrites article_category from file" do
|
||||||
check('articles_convert_units')
|
let!(:article_category) { create(:article_category, name: "Fruit") }
|
||||||
find('input[type="submit"]').click
|
let(:article) { create(:article, supplier: supplier, name: 'Tomatoes', order_number: 1, unit: '250 g', article_category: article_category) }
|
||||||
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
|
|
||||||
find('input[type="submit"]').click
|
it do
|
||||||
article.reload
|
find(:css, '#articles_update_category[value="1"]').set(true) # check take over category from file
|
||||||
expect([article.unit, article.unit_quantity, article.price]).to eq ['250 g', 40, 0.6]
|
find('input[type="submit"]').click #upload file
|
||||||
|
find('input[type="submit"]').click #submit changes
|
||||||
|
expect(supplier.articles.count).to eq 1
|
||||||
|
expect(supplier.articles.first.article_category.name).to eq "Vegetables"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "can remove an existing article" do
|
||||||
|
let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 99999) }
|
||||||
|
|
||||||
|
it do
|
||||||
|
check('articles_outlist_absent')
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
expect(find("#outlisted_articles_#{article.id}", visible: :all)).to be_present
|
||||||
|
|
||||||
|
all("tr select > option")[1].select_option
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
expect(article.reload.deleted?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "can convert units when updating" do
|
||||||
|
let!(:article) { create(:article, supplier: supplier, order_number: 1, unit: '250 g') }
|
||||||
|
|
||||||
|
it do
|
||||||
|
check('articles_convert_units')
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
article.reload
|
||||||
|
expect([article.unit, article.unit_quantity, article.price]).to eq ['250 g', 40, 0.6]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
require_relative '../spec_helper'
|
|
||||||
|
|
||||||
feature GroupOrderInvoice, js: true do
|
|
||||||
let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] }
|
|
||||||
let(:user) { create :user, groups: [create(:ordergroup)] }
|
|
||||||
let(:article) { create :article, unit_quantity: 1 }
|
|
||||||
let(:order) { create :order, supplier: article.supplier, article_ids: [article.id], ends: Time.now } # need to ref article
|
|
||||||
let(:go) { create :group_order, order: order, ordergroup: user.ordergroup}
|
|
||||||
let(:oa) { order.order_articles.find_by_article_id(article.id) }
|
|
||||||
let(:ftt) { create :financial_transaction_type }
|
|
||||||
let(:goa) { create :group_order_article, group_order: go, order_article: oa }
|
|
||||||
|
|
||||||
include ActiveJob::TestHelper
|
|
||||||
|
|
||||||
before { login admin }
|
|
||||||
|
|
||||||
after { clear_enqueued_jobs }
|
|
||||||
|
|
||||||
it 'does not enqueue MailerJob when order is settled if tax_number or options not set' do
|
|
||||||
goa.update_quantities 2, 0
|
|
||||||
oa.update_results!
|
|
||||||
visit confirm_finance_order_path(id: order.id)
|
|
||||||
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
|
||||||
expect(NotifyGroupOrderInvoiceJob).not_to have_been_enqueued
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'enqueues MailerJob when order is settled if tax_number or options are set' do
|
|
||||||
goa.update_quantities 2, 0
|
|
||||||
oa.update_results!
|
|
||||||
order.reload
|
|
||||||
FoodsoftConfig[:group_order_invoices] = { use_automatic_invoices: true }
|
|
||||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
|
||||||
visit confirm_finance_order_path(id: order.id, type: ftt)
|
|
||||||
expect(page).to have_selector(:link_or_button, I18n.t('finance.balancing.confirm.clear'))
|
|
||||||
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
|
||||||
expect(NotifyGroupOrderInvoiceJob).to have_been_enqueued
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'generates Group Order Invoice when order is closed if tax_number is set' do
|
|
||||||
goa.update_quantities 2, 0
|
|
||||||
oa.update_results!
|
|
||||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
|
||||||
order.update!(state: 'closed')
|
|
||||||
order.reload
|
|
||||||
visit finance_order_index_path
|
|
||||||
expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate'))
|
|
||||||
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate')
|
|
||||||
expect(GroupOrderInvoice.all.count).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'generates multiple Group Order Invoice for order when order is closed if tax_number is set' do
|
|
||||||
goa.update_quantities 2, 0
|
|
||||||
oa.update_results!
|
|
||||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
|
||||||
order.update!(state: 'closed')
|
|
||||||
order.reload
|
|
||||||
visit finance_order_index_path
|
|
||||||
expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date'))
|
|
||||||
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date')
|
|
||||||
expect(GroupOrderInvoice.all.count).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not generate Group Order Invoice when order is closed if tax_number not set' do
|
|
||||||
goa.update_quantities 2, 0
|
|
||||||
oa.update_results!
|
|
||||||
order.update!(state: 'closed')
|
|
||||||
order.reload
|
|
||||||
visit finance_order_index_path
|
|
||||||
expect(page).to have_content(I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set'))
|
|
||||||
end
|
|
||||||
end
|
|
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
|
|
@ -1,59 +0,0 @@
|
||||||
require_relative '../spec_helper'
|
|
||||||
|
|
||||||
describe GroupOrderInvoice do
|
|
||||||
let(:user) { create :user, groups: [create(:ordergroup)] }
|
|
||||||
let(:supplier) { create :supplier }
|
|
||||||
let(:article) { create :article, supplier: supplier }
|
|
||||||
let(:order) { create :order }
|
|
||||||
let(:group_order) { create :group_order, order: order, ordergroup: user.ordergroup }
|
|
||||||
|
|
||||||
describe 'erroneous group order invoice' do
|
|
||||||
let(:goi) { create :group_order_invoice, group_order_id: group_order.id }
|
|
||||||
it 'does not create group order invoice if tax_number not set' do
|
|
||||||
expect { goi }.to raise_error(ActiveRecord::RecordInvalid, /.*/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'valid group order invoice' do
|
|
||||||
before do
|
|
||||||
FoodsoftConfig[:contact][:tax_number] = 123_457_8
|
|
||||||
end
|
|
||||||
|
|
||||||
invoice_number1 = Time.now.strftime("%Y%m%d") + '0001'
|
|
||||||
invoice_number2 = Time.now.strftime("%Y%m%d") + '0002'
|
|
||||||
|
|
||||||
let(:user2) { create :user, groups: [create(:ordergroup)] }
|
|
||||||
|
|
||||||
let(:goi1) { create :group_order_invoice, group_order_id: group_order.id }
|
|
||||||
let(:goi2) { create :group_order_invoice, group_order_id: group_order.id }
|
|
||||||
|
|
||||||
let(:group_order2) { create :group_order, order: order, ordergroup: user2.ordergroup }
|
|
||||||
|
|
||||||
let(:goi3) { create :group_order_invoice, group_order_id: group_order2.id }
|
|
||||||
let(:goi4) { create :group_order_invoice, group_order_id: group_order2.id, invoice_number: invoice_number1 }
|
|
||||||
|
|
||||||
it 'creates group order invoice if tax_number is set' do
|
|
||||||
expect(goi1).to be_valid
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sets invoice_number according to date' do
|
|
||||||
number = Time.now.strftime("%Y%m%d") + '0001'
|
|
||||||
expect(goi1.invoice_number).to eq(number.to_i)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'fails to create if group_order_id is used multiple times for creation' do
|
|
||||||
expect(goi1.group_order.id).to eq(group_order.id)
|
|
||||||
expect { goi2 }.to raise_error(ActiveRecord::RecordNotUnique)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates two different group order invoice with different invoice_numbers' do
|
|
||||||
expect(goi1.invoice_number).to eq(invoice_number1.to_i)
|
|
||||||
expect(goi3.invoice_number).to eq(invoice_number2.to_i)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'fails to create two different group order invoice with same invoice_numbers' do
|
|
||||||
goi1
|
|
||||||
expect { goi4 }.to raise_error(ActiveRecord::RecordInvalid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -11,7 +11,7 @@ describe Supplier do
|
||||||
options = { filename: 'foodsoft_file_01.csv' }
|
options = { filename: 'foodsoft_file_01.csv' }
|
||||||
options[:outlist_absent] = true
|
options[:outlist_absent] = true
|
||||||
options[:convert_units] = true
|
options[:convert_units] = true
|
||||||
updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file(Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), options)
|
updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file(Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), "foodsoft", options)
|
||||||
expect(new_articles.length).to be > 0
|
expect(new_articles.length).to be > 0
|
||||||
expect(updated_article_pairs.first[1][:name]).to eq 'Tomaten'
|
expect(updated_article_pairs.first[1][:name]).to eq 'Tomaten'
|
||||||
expect(outlisted_articles.first).to eq article2
|
expect(outlisted_articles.first).to eq article2
|
||||||
|
|