diff --git a/mysite/common_settings.py b/mysite/common_settings.py index 8c0222738d8ff65ca182cbb70621d7b1e516a871..5383d100706921811b1ba9869adfc9d36e506089 100644 --- a/mysite/common_settings.py +++ b/mysite/common_settings.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -import os - from settings import * +import os +import sys gettext = lambda s: s PROJECT_PATH = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0] PROJECT_DIR = os.path.realpath(os.path.dirname(__file__)) os.sys.path.insert(0, os.path.dirname(PROJECT_DIR)) -# ##################### DEBUG +###################### DEBUG # Defined into /etc/uwsgi/apps-available/*.ini DEBUG = True if os.getenv('DJANGO_SETTINGS_MODULE_DEBUG', '') == 'True' else False TEMPLATE_DEBUG = DEBUG +DEBUG_PROPAGATE_EXCEPTIONS = DEBUG ADMINS = ( ( os.getenv('DJANGO_SETTINGS_MODULE_ADMIN_NAME', ''), @@ -47,6 +48,10 @@ EMAIL_HOST_USER = os.getenv('DJANGO_SETTINGS_MODULE_EMAIL_HOST_USER', '') EMAIL_HOST_PASSWORD = os.getenv('DJANGO_SETTINGS_MODULE_EMAIL_HOST_PASSWORD', '') EMAIL_PORT = os.getenv('DJANGO_SETTINGS_MODULE_EMAIL_PORT', '') EMAIL_USE_TLS = True if os.getenv('DJANGO_SETTINGS_MODULE_EMAIL_USE_TLS', '') == 'True' else False +if not EMAIL_USE_TLS: + EMAIL_USE_SSL = True if os.getenv('DJANGO_SETTINGS_MODULE_EMAIL_USE_SSL', '') == 'True' else False +else: + EMAIL_USE_SSL = False # if DEBUG: # EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' ###################### I18N @@ -64,10 +69,10 @@ DECIMAL_SEPARATOR = ',' ##################### Django & Django CMS LANGUAGES = [ - ('fr', 'Français'), - ('nl', 'Neederlands'), - ('en', 'English'), -] + ('fr', u'Français'), + ('nl', u'Neederlands'), + ('en', u'English'), + ('it', u'italiano'), ] CMS_LANGUAGES = { 'default': { @@ -82,19 +87,53 @@ LOCALE_PATHS = ( os.path.join(PROJECT_DIR, "locale"), ) +INSTALLED_APPS = ( + 'django.contrib.sites', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.sitemaps', + 'django.contrib.formtools', + 'djangocms_text_ckeditor', # note this needs to be above the 'cms' entry + 'cms', + "treebeard", + 'mptt', + 'menus', + 'sekizai', + 'djangocms_admin_style', # note this needs to be above the 'django.contrib.admin' entry + 'django.contrib.admin', + 'django_mptt_admin', + 'filer', + 'easy_thumbnails', + 'cmsplugin_filer_file', + 'cmsplugin_filer_folder', + 'cmsplugin_filer_link', + 'cmsplugin_filer_image', + 'cmsplugin_filer_video', + 'reversion', + 'password_reset', + 'parler', + +) + MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.middleware.common.BrokenLinkEmailsMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.doc.XViewMiddleware', - 'django.middleware.common.CommonMiddleware', - # 'cms.middleware.language.LanguageCookieMiddleware', Disable to avoid cookies advertising requirement - 'cms.middleware.user.CurrentUserMiddleware', 'cms.middleware.page.CurrentPageMiddleware', + 'cms.middleware.user.CurrentUserMiddleware', 'cms.middleware.toolbar.ToolbarMiddleware', + # 'cms.middleware.language.LanguageCookieMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ) @@ -110,85 +149,61 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'sekizai.context_processors.sekizai', ) -INSTALLED_APPS = ( - 'django.contrib.sites', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.sitemaps', - 'django.contrib.formtools', - 'djangocms_text_ckeditor', # note this needs to be above the 'cms' entry - 'cms', - 'mptt', - 'menus', - 'south', - 'sekizai', - 'djangocms_admin_style', # note this needs to be above - # the 'django.contrib.admin' entry - 'django.contrib.admin', - 'adminsortable', - # 'hvad', - 'filer', - 'easy_thumbnails', - - 'cmsplugin_filer_file', - 'cmsplugin_filer_folder', - 'cmsplugin_filer_image', - 'cmsplugin_filer_video', - 'cmsplugin_filer_link', - 'reversion', - 'password_reset', - +TEMPLATE_LOADERS = ( + ('django.template.loaders.cached.Loader', ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + )), ) +MIGRATION_MODULES = { + 'cms': 'cms.migrations_django', + 'menus': 'menus.migrations_django', + 'filer': 'filer.migrations_django', + 'djangocms_text_ckeditor': 'djangocms_text_ckeditor.migrations_django', + 'cmsplugin_filer_file': 'cmsplugin_filer_file.migrations_django', + 'cmsplugin_filer_folder': 'cmsplugin_filer_folder.migrations_django', + 'cmsplugin_filer_link': 'cmsplugin_filer_link.migrations_django', + 'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django', + 'cmsplugin_filer_video': 'cmsplugin_filer_video.migrations_django', +} + CMS_PERMISSION = False # When set to True, don't forget 'cms.middleware.user.CurrentUserMiddleware' CMS_PUBLIC_FOR = 'all' +# CMS_PUBLIC_FOR = 'staff' CMS_SHOW_START_DATE = False CMS_SHOW_END_DATE = False CMS_SEO_FIELDS = False CMS_URL_OVERWRITE = True CMS_MENU_TITLE_OVERWRITE = True CMS_REDIRECTS = True -LOGIN_URL = "/go_repanier/" -LOGIN_REDIRECT_URL = "/" -LOGOUT_URL = "/leave_repanier/" CKEDITOR_SETTINGS = { 'language': '{{ language }}', - 'toolbar_CMS': [ - ['Undo', 'Redo'], - ['cmsplugins', '-', 'ShowBlocks'], - # ['Format', 'Styles'], - ['Format', 'Templates'], - ['TextColor', 'BGColor', '-', 'PasteText'], #, 'PasteFromWord'], - ['Maximize', ''], - '/', - ['Bold', 'Italic', 'Underline', '-', 'Subscript', 'Superscript', '-', 'RemoveFormat'], - ['JustifyLeft', 'JustifyCenter', 'JustifyRight'], - ['HorizontalRule'], - ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Table'], - ['Source'] - ], - 'toolbar_HTMLField': [ - ['Undo', 'Redo'], - ['ShowBlocks', 'Format'], - ['TextColor', 'BGColor', '-', 'PasteText'], #, 'PasteFromWord'], - ['Maximize', ''], - '/', - ['Bold', 'Italic', 'Underline', '-', 'Subscript', 'Superscript', '-', 'RemoveFormat'], - ['JustifyLeft', 'JustifyCenter', 'JustifyRight'], - ['HorizontalRule'], - ['Link', 'Unlink'], - ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Table'], - ['Source'] - ], + 'toolbar_CMS': [ + ['Undo', 'Redo'], + ['cmsplugins', '-', 'ShowBlocks'], + ['Format', 'Templates'], + ['TextColor', 'BGColor', '-', 'PasteText'], + ['Maximize', ''], + '/', + ['Bold', 'Italic', 'Underline', '-', 'Subscript', 'Superscript', '-', 'RemoveFormat'], + ['JustifyLeft', 'JustifyCenter', 'JustifyRight'], + ['HorizontalRule'], + ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Table'], + ['Source'] + ], + 'toolbar_HTMLField': [ + ['Format', 'Bold', 'Italic', 'TextColor', '-', 'NumberedList', 'BulletedList', 'RemoveFormat'], + ['Preview', 'Cut', 'Copy', 'PasteText', 'Link', '-', 'Undo', 'Redo'], + ['Maximize', ''] + ], + 'forcePasteAsPlainText': 'true', 'skin': 'moono', # 'stylesSet' : 'my_styles:%sjs/ckeditor-styles.js' % STATIC_URL, # 'stylesSet' : [], - 'extraPlugins': 'cmsplugins,templates', - 'format_tags': 'p;h1;h2;h3;h4;h5;blockquote;mutted;success;info;danger;heart;infosign;warningsign;pushpin;div', + # 'extraPlugins': 'cmsplugins', + 'format_tags': 'p;h4;h5;blockquote;mutted;success;info;danger;heart;pushpin', 'format_blockquote': {'element': 'blockquote', 'name': 'Blockquote'}, 'format_heart': {'element': 'span', 'attributes': {'class': 'glyphicon glyphicon-heart-empty'}}, 'format_infosign': {'element': 'span', 'attributes': {'class': 'glyphicon glyphicon-info-sign'}}, @@ -203,15 +218,36 @@ CKEDITOR_SETTINGS = { # 'contentsCss' : '//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css', 'contentsCss': '%sbootstrap/css/bootstrap.css' % STATIC_URL, # 'extraAllowedContent' : '*(*)', - # 'removeFormatTags' : 'b,big,code,del,dfn,em,font,i,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,u,var' + 'removeFormatTags': 'big,code,del,dfn,em,font,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,u,var', + 'basicEntities': False, + 'entities': False, + 'removePlugins': 'elementspath', +} + +CKEDITOR_SETTINGS_MODEL2 = { + 'language': '{{ language }}', + 'toolbar_HTMLField': [ + ['Format', 'Bold', 'Italic', 'TextColor', '-', 'NumberedList', 'BulletedList', 'RemoveFormat'], + ['Preview', 'Cut', 'Copy', 'PasteText', 'Link', '-', 'Undo', 'Redo'], + ['Maximize', ''] + ], + 'forcePasteAsPlainText': 'true', + 'skin': 'moono', + 'format_tags': 'p;h4;h5', + 'contentsCss': '%sbootstrap/css/bootstrap.css' % STATIC_URL, + 'removeFormatTags': 'big,code,del,dfn,em,font,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,u,var', + 'basicEntities': False, + 'entities': False, + 'removePlugins': 'elementspath', } TEXT_ADDITIONAL_TAGS = ('span',) -TEXT_ADDITIONAL_ATTRIBUTES = ('class',) -# TEXT_HTML_SANITIZE = False +TEXT_ADDITIONAL_ATTRIBUTES = ('class',) +TEXT_HTML_SANITIZE = True # TEXT_SAVE_IMAGE_FUNCTION = 'cmsplugin_filer_image.integrations.ckeditor.create_image_plugin' # TEXT_SAVE_IMAGE_FUNCTION = 'djangocms_text_ckeditor.picture_save.create_picture_plugin' TEXT_SAVE_IMAGE_FUNCTION = None +TEXT_PLUGINS_INTEGRATION = 'buttons' FILER_ENABLE_LOGGING = False FILER_IMAGE_USE_ICON = True @@ -219,7 +255,7 @@ FILER_ALLOW_REGULAR_USERS_TO_ADD_ROOT_FOLDERS = True FILER_ENABLE_PERMISSIONS = False FILER_IS_PUBLIC_DEFAULT = True FILER_SUBJECT_LOCATION_IMAGE_DEBUG = True -FILER_DEBUG = DEBUG +FILER_DEBUG = False THUMBNAIL_PROCESSORS = ( 'easy_thumbnails.processors.colorspace', @@ -228,14 +264,14 @@ THUMBNAIL_PROCESSORS = ( 'filer.thumbnail_processors.scale_and_crop_with_subject_location', 'easy_thumbnails.processors.filters', ) -THUMBNAIL_DEBUG = DEBUG +THUMBNAIL_HIGH_RESOLUTION = True +THUMBNAIL_DEBUG = FILER_DEBUG # https://docs.djangoproject.com/en/1.5/howto/static-files/ STATIC_ROOT = os.path.join(PROJECT_DIR, "collect-static") STATIC_URL = "/static/" MEDIA_URL = "/media/" USE_X_FORWARDED_HOST = True -SEND_BROKEN_LINK_EMAILS = True SESSION_EXPIRE_AT_BROWSER_CLOSE = True SESSION_ENGINE = "django.contrib.sessions.backends.file" SESSION_COOKIE_HTTPONLY = True @@ -248,16 +284,20 @@ AUTHENTICATION_BACKENDS = ('repanier.auth_backend.RepanierCustomBackend',) # ADMIN_LOGIN = 'pise' # ADMIN_PASSWORD = 'raspberry' INSTALLED_APPS += ( -'repanier', + 'repanier', ) - +LOGIN_URL = "/repanier/go_repanier/" +LOGIN_REDIRECT_URL = "/" +LOGOUT_URL = "/repanier/leave_repanier/" ################# Django_crispy_forms -# INSTALLED_APPS += ( -# 'crispy_forms', -# ) +INSTALLED_APPS += ( + 'crispy_forms', + # 'crispy_forms_foundation', +) -# CRISPY_TEMPLATE_PACK = "bootstrap3" +CRISPY_TEMPLATE_PACK = "bootstrap3" +# # CRISPY_TEMPLATE_PACK = "foundation" # JSON_MODULE = 'ujson' ################# Django_compressor @@ -311,33 +351,35 @@ COMPRESS_OFFLINE = False CACHE_MIDDLEWARE_ALIAS = 'default' CACHE_MIDDLEWARE_SECONDS = 3600 -CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', - 'LOCATION': '/var/tmp/django_cache', - 'TIMEOUT': 300, - 'OPTIONS': { - 'MAX_ENTRIES': 1000, - 'CULL_FREQUENCY': 3 - } - } -} +# CACHES = { +# 'default': { +# 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', +# 'LOCATION': '/var/tmp/django_cache', +# 'TIMEOUT': 300, +# 'OPTIONS': { +# 'MAX_ENTRIES': 1000, +# 'CULL_FREQUENCY': 3 +# } +# } +# } CMS_CACHE_DURATIONS = { 'content': 300, # default 60 'menus': 3600, # default 3600 'permissions': 3600 # default: 3600 } -CMS_PAGE_CACHE = True -CMS_PLACEHOLDER_CACHE = True -CMS_PLUGIN_CACHE = True -SOUTH_MIGRATION_MODULES = { - 'easy_thumbnails': 'easy_thumbnails.south_migrations', -} +# SOUTH_MIGRATION_MODULES = { +# 'easy_thumbnails': 'easy_thumbnails.south_migrations', +# } ###################### EASYMAP #EASY_MAPS_CENTER = ( 50.630545,3.776955 ) +##################### DECIMAL +from decimal import getcontext, ROUND_HALF_UP + +getcontext().rounding = ROUND_HALF_UP + #INSTALLED_APPS += ( # 'easy_maps', #) @@ -348,8 +390,8 @@ SOUTH_MIGRATION_MODULES = { # l = logging.getLogger('django.db.backends') # l.setLevel(logging.DEBUG) # l.addHandler(logging.StreamHandler()) - - +# +# # LOGGING = { # 'version': 1, # 'disable_existing_loggers': False, diff --git a/mysite/locale/fr/LC_MESSAGES/django.mo b/mysite/locale/fr/LC_MESSAGES/django.mo index d5b9a1c4741f3a8ed3f245aae94c1173ef7a0e8a..5c85d053c5c767c1abc3b6d07d2c15c1f58278c0 100644 Binary files a/mysite/locale/fr/LC_MESSAGES/django.mo and b/mysite/locale/fr/LC_MESSAGES/django.mo differ diff --git a/mysite/locale/fr/LC_MESSAGES/django.po b/mysite/locale/fr/LC_MESSAGES/django.po index c9e745a9236c7928d8efc5b3689c507c2e3ec76d..bb35132688e871fd9dc85da1600772ea55a5a41a 100644 --- a/mysite/locale/fr/LC_MESSAGES/django.po +++ b/mysite/locale/fr/LC_MESSAGES/django.po @@ -1,114 +1,86 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. +# FIRST AUTHOR pcolmant@gmail.com, 2014. # msgid "" msgstr "" "Project-Id-Version: Repanier\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-08-14 10:15+0200\n" -"PO-Revision-Date: 2014-06-03 18:24+0100\n" +"POT-Creation-Date: 2015-05-20 19:55+0200\n" +"PO-Revision-Date: 2015-05-21 21:19+0100\n" "Last-Translator: Patrick Colmant \n" -"Language-Team: LANGUAGE \n" +"Language-Team: Patrick Colmant \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 1.6.4\n" +"X-Generator: Poedit 1.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" -#: ptidej_settings.py:18 +#: ptidej_settings.py:34 msgid "Homepage" msgstr "Page d'accueil" -#: ptidej_settings.py:19 +#: ptidej_settings.py:35 msgid "Secondary Page" msgstr "Page interne" -#: ptidej_settings.py:25 +#: ptidej_settings.py:41 msgid "French" msgstr "Fançais" -#: ptidej_settings.py:34 +#: ptidej_settings.py:76 msgid "Hero" msgstr "Héro" -#: ptidej_settings.py:53 +#: ptidej_settings.py:95 msgid "Column 1" msgstr "Colonne 1" -#: ptidej_settings.py:75 +#: ptidej_settings.py:115 msgid "Column 2" msgstr "Colonne 2" -#: ptidej_settings.py:97 +#: ptidej_settings.py:135 msgid "Column 3" msgstr "Colonne 3" -#: ptidej_settings.py:122 +#: ptidej_settings.py:160 msgid "Content" msgstr "Contenu" -#: ptidej_settings.py:137 +#: ptidej_settings.py:175 msgid "Footer" msgstr "Pide de page" -#: ptidej_settings.py:153 -msgid "info" -msgstr "info" - -#: ptidej_settings.py:154 -msgid "new" -msgstr "nouveau" - -#: ptidej_settings.py:155 -msgid "hint" -msgstr "atteindre" - -#: ptidej_settings.py:159 -msgid "normal" -msgstr "normal" - -#: ptidej_settings.py:160 -msgid "2x" -msgstr "2x" - -#: ptidej_settings.py:161 -msgid "3x" -msgstr "3x" - -#: ptidej_settings.py:162 -msgid "4x" -msgstr "4x" - -#: templates/base.html:35 templates/base_wo_cms_toolbar.html:35 +#: templates/base.html:36 templates/base_wo_cms_toolbar.html:33 msgid "Skip to main content" msgstr "Aller au contenu principal" -#: templates/base.html:42 templates/base_wo_cms_toolbar.html:41 +#: templates/base.html:43 templates/base_wo_cms_toolbar.html:39 msgid "Toggle navigation" msgstr "Basculer la navigation" -#: templates/base.html:62 templates/base_wo_cms_toolbar.html:61 +#: templates/base.html:63 templates/base_wo_cms_toolbar.html:58 msgid "Welkom" msgstr "Bienvenue" -#: templates/base.html:64 templates/base_wo_cms_toolbar.html:63 +#: templates/base.html:65 templates/base_wo_cms_toolbar.html:60 #: templates/registration/logged_out.html:7 msgid "Logout" msgstr "Deconnexion" -#: templates/base.html:69 templates/base_wo_cms_toolbar.html:71 -#: templates/registration/login.html:7 templates/registration/login.html:32 +#: templates/base.html:70 templates/registration/login.html:8 +#: templates/registration/login.html:54 msgid "Login" msgstr "Connexion" -#: templates/base.html:74 templates/base_wo_cms_toolbar.html:76 +#: templates/base.html:76 templates/base_wo_cms_toolbar.html:71 msgid "Language" msgstr "Langue" -#: templates/base.html:87 templates/base_wo_cms_toolbar.html:90 +#: templates/base.html:90 templates/base_wo_cms_toolbar.html:86 msgid "" "It will be easier for you to crawl this website with a newer browser. For " "example" @@ -116,15 +88,15 @@ msgstr "" "Votre navigateur est trop ancien pour visiter ce site confortablement. Nous " "vous recommandons de le mettre à jour ou d'utiliser par exemple " -#: templates/base.html:87 templates/base_wo_cms_toolbar.html:90 +#: templates/base.html:90 templates/base_wo_cms_toolbar.html:86 msgid "or" msgstr "ou" -#: templates/base.html:95 templates/base_wo_cms_toolbar.html:99 +#: templates/base.html:98 msgid "Contact" msgstr "Contact" -#: templates/base.html:95 templates/base_wo_cms_toolbar.html:99 +#: templates/base.html:98 msgid "Participer" msgstr "Participer" @@ -140,11 +112,14 @@ msgstr "Réinitialisation du mot de passe" #: templates/password_reset/recovery_done.html:11 msgid "" -"Your password has successfully been reset. You can use it right now on the " -"login page." +"Your password has successfully been reset. You can use it right now on the" msgstr "" -"Votre mote de passe a été mis à jour. Vous pouvez vous connecter dès à présent." +"Votre mot de passe a été réinitialisé avec succès. Vous pouvez l'" +"utiliser dès maintenant sur la" + +#: templates/password_reset/recovery_done.html:11 +msgid "login page" +msgstr "Page de connexion" #: templates/password_reset/recovery_email.txt:1 #, python-format @@ -179,11 +154,11 @@ msgstr "" msgid "Password recovery on %(domain)s" msgstr "Réinitialisation du mot de passe sur %(domain)s" -#: templates/password_reset/recovery_form.html:15 +#: templates/password_reset/recovery_form.html:14 msgid "Username or Email" msgstr "Nom d'utilisateur ou Email" -#: templates/password_reset/recovery_form.html:17 +#: templates/password_reset/recovery_form.html:16 msgid "Recover my password" msgstr "Réinitialiser mon mot de passe" @@ -204,6 +179,14 @@ msgstr "" "mot de passe." #: templates/password_reset/reset.html:18 +msgid "New password" +msgstr "Enregistrer le nouveau mot de passe" + +#: templates/password_reset/reset.html:22 +msgid "New password (confirm)" +msgstr "Confirmer le nouveau mot de passe" + +#: templates/password_reset/reset.html:25 msgid "Set new password" msgstr "Enregistrer le nouveau mot de passe" @@ -224,22 +207,77 @@ msgstr "" "Vous êtes maintenant déconnecté. Vous pouvez vous re connecter ou vous rendre à la page d'accueil." -#: templates/registration/login.html:16 -msgid "Please correct the error below." -msgstr "Veuillez corriger l'erreur ci-dessous." +#: templates/registration/login.html:3 +msgid "Too many attempt." +msgstr "Trop de tentatives." -#: templates/registration/login.html:16 -msgid "Please correct the errors below." -msgstr "Veuillez corriger les erreurs ci-dessous." - -#: templates/registration/login.html:20 templates/registration/login.html:22 +#: templates/registration/login.html:23 templates/registration/login.html:31 msgid "Username" msgstr "Nom d'utilisateur" -#: templates/registration/login.html:26 templates/registration/login.html:28 +#: templates/registration/login.html:33 templates/registration/login.html:41 msgid "Password" msgstr "Mot de passe" -#: templates/registration/login.html:33 +#: templates/registration/login.html:48 +msgid "Validation code (2 latest digits of your phone number)" +msgstr "Code de validation (4 derniers chiffres de votre ✆)" + +#: templates/registration/login.html:49 +msgid "Validation code" +msgstr "Code de validation" + +#: templates/registration/login.html:55 msgid "Password lost" msgstr "Mot de passe perdu ?" + +#~ msgid "Permanence" +#~ msgstr "Permanence" + +#~ msgid "Permanences" +#~ msgstr "Permanences" + +#~ msgid "Permanence on " +#~ msgstr "Permanence du " + +#~ msgid "Please correct the error below." +#~ msgstr "Veuillez corriger l'erreur ci-dessous." + +#~ msgid "Please correct the errors below." +#~ msgstr "Veuillez corriger les erreurs ci-dessous." + +#~ msgid "Closure" +#~ msgstr "Clôture" + +#~ msgid "Closures" +#~ msgstr "Clôtures" + +#~ msgid "Closure on " +#~ msgstr "Clôture du " + +#~ msgid "Dutch" +#~ msgstr "Dutch" + +#~ msgid "English" +#~ msgstr "English" + +#~ msgid "info" +#~ msgstr "info" + +#~ msgid "new" +#~ msgstr "nouveau" + +#~ msgid "hint" +#~ msgstr "atteindre" + +#~ msgid "normal" +#~ msgstr "normal" + +#~ msgid "2x" +#~ msgstr "2x" + +#~ msgid "3x" +#~ msgstr "3x" + +#~ msgid "4x" +#~ msgstr "4x" diff --git a/mysite/locale/fr/LC_MESSAGES/djangojs.mo b/mysite/locale/fr/LC_MESSAGES/djangojs.mo index ceba0245d2148759dc4766080619d1b1c44bb876..891d4f7510c250b8f6ed045fa9f207b8fab3835a 100644 Binary files a/mysite/locale/fr/LC_MESSAGES/djangojs.mo and b/mysite/locale/fr/LC_MESSAGES/djangojs.mo differ diff --git a/mysite/locale/fr/LC_MESSAGES/djangojs.po b/mysite/locale/fr/LC_MESSAGES/djangojs.po index 94397ab9a837b9ffc81c5fdcc935fe5a114da31b..80bcf4b13619588a33d9dbb75f76e07ad8903411 100644 --- a/mysite/locale/fr/LC_MESSAGES/djangojs.po +++ b/mysite/locale/fr/LC_MESSAGES/djangojs.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Repanier\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-08-14 10:15+0200\n" -"PO-Revision-Date: 2014-03-18 21:20+0100\n" +"POT-Creation-Date: 2015-04-02 08:38+0200\n" +"PO-Revision-Date: 2015-04-02 08:41+0100\n" "Last-Translator: Patrick Colmant \n" "Language-Team: LANGUAGE \n" "Language: fr\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 1.6.4\n" +"X-Generator: Poedit 1.7.1\n" #: collect-static/admin/js/SelectFilter2.js:45 #, c-format @@ -82,15 +82,15 @@ msgstr "Tout supprimer" msgid "Click to remove all chosen %s at once." msgstr "Cliquer pour supprimer tous les %s choisis en même temps." -#: collect-static/admin/js/actions.js:18 +#: collect-static/admin/js/actions.js:22 #: collect-static/admin/js/actions.min.js:1 msgid "%(sel)s of %(cnt)s selected" msgid_plural "%(sel)s of %(cnt)s selected" msgstr[0] "%(sel)s parmi %(cnt)s sélectionné" msgstr[1] "%(sel)s parmi %(cnt)s sélectionnés" -#: collect-static/admin/js/actions.js:109 -#: collect-static/admin/js/actions.min.js:5 +#: collect-static/admin/js/actions.js:114 +#: collect-static/admin/js/actions.min.js:4 msgid "" "You have unsaved changes on individual editable fields. If you run an " "action, your unsaved changes will be lost." @@ -98,7 +98,7 @@ msgstr "" "Vous voulez quitter sans avoir sauvegarder les modifications. Elles seront " "perdues." -#: collect-static/admin/js/actions.js:121 +#: collect-static/admin/js/actions.js:126 #: collect-static/admin/js/actions.min.js:5 msgid "" "You have selected an action, but you haven't saved your changes to " @@ -108,8 +108,8 @@ msgstr "" "Vous voulez sauvegarder sans avoir fait de modification. Ne vous êtes-vous " "pas trompé de bouton..." -#: collect-static/admin/js/actions.js:123 -#: collect-static/admin/js/actions.min.js:6 +#: collect-static/admin/js/actions.js:128 +#: collect-static/admin/js/actions.min.js:5 msgid "" "You have selected an action, and you haven't made any changes on individual " "fields. You're probably looking for the Go button rather than the Save " @@ -118,76 +118,102 @@ msgstr "" "Vous voulez sauvegarder sans avoir fait de modification. Ne vous êtes-vous " "pas trompé de bouton..." -#: collect-static/admin/js/calendar.js:8 -msgid "" -"January February March April May June July August September October November " -"December" -msgstr "" -"janvier février mars avril mai juin juillet août septembre octobre novembre " -"décembre" - -#: collect-static/admin/js/calendar.js:9 -msgid "S M T W T F S" -msgstr "D L M M J V S" - -#: collect-static/admin/js/collapse.js:8 -#: collect-static/admin/js/collapse.js:19 -#: collect-static/admin/js/collapse.min.js:1 -msgid "Show" -msgstr "Afficher" +#: collect-static/admin/js/admin/DateTimeShortcuts.js:79 +#, c-format +msgid "Note: You are %s hour ahead of server time." +msgid_plural "Note: You are %s hours ahead of server time." +msgstr[0] "Note: Vous êtes %s heure à l'avance sur l'heure du serveur." +msgstr[1] "Note: Vous êtes %s heures à l'avance sur l'heure du serveur." -#: collect-static/admin/js/collapse.js:16 -#: collect-static/admin/js/collapse.min.js:1 -msgid "Hide" -msgstr "Masquer" +#: collect-static/admin/js/admin/DateTimeShortcuts.js:87 +#, c-format +msgid "Note: You are %s hour behind server time." +msgid_plural "Note: You are %s hours behind server time." +msgstr[0] "Note: Vous êtes %s heure en retard sur l'heure du serveur." +msgstr[1] "Note: Vous êtes %s heures en retard sur l'heure du serveur." -#: collect-static/admin/js/admin/DateTimeShortcuts.js:52 -#: collect-static/admin/js/admin/DateTimeShortcuts.js:88 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:114 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:149 msgid "Now" msgstr "Maintenant" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:56 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:118 msgid "Clock" msgstr "Horloge" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:84 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:146 msgid "Choose a time" msgstr "Choisir une heure" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:89 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:150 msgid "Midnight" msgstr "Minuit" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:90 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:151 msgid "6 a.m." msgstr "6 a.m." -#: collect-static/admin/js/admin/DateTimeShortcuts.js:91 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:152 msgid "Noon" msgstr "Midi" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:95 -#: collect-static/admin/js/admin/DateTimeShortcuts.js:208 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:156 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:276 msgid "Cancel" msgstr "Annuler" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:148 -#: collect-static/admin/js/admin/DateTimeShortcuts.js:201 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:216 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:269 msgid "Today" msgstr "Aujourd'hui" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:152 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:220 msgid "Calendar" msgstr "Calendrier" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:199 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:267 msgid "Yesterday" msgstr "Hier" -#: collect-static/admin/js/admin/DateTimeShortcuts.js:203 +#: collect-static/admin/js/admin/DateTimeShortcuts.js:271 msgid "Tomorrow" msgstr "Demain" +#: collect-static/admin/js/calendar.js:8 +msgid "" +"January February March April May June July August September October November " +"December" +msgstr "" +"janvier février mars avril mai juin juillet août septembre octobre novembre " +"décembre" + +#: collect-static/admin/js/calendar.js:9 +msgid "S M T W T F S" +msgstr "D L M M J V S" + +#: collect-static/admin/js/collapse.js:8 +#: collect-static/admin/js/collapse.js:19 +#: collect-static/admin/js/collapse.min.js:1 +msgid "Show" +msgstr "Afficher" + +#: collect-static/admin/js/collapse.js:16 +#: collect-static/admin/js/collapse.min.js:1 +msgid "Hide" +msgstr "Masquer" + #: collect-static/cms/js/modules/cms.changeform.js:60 msgid "Are you sure you want to change tabs without saving the page first?" msgstr "Etes-vous sur de changer d'onglet sans avoir d'abord sauvegardé?" + +#: collect-static/treebeard/treebeard-admin.js:158 +msgid "Abort" +msgstr "Abandonner" + +#: collect-static/treebeard/treebeard-admin.js:180 +msgid "As Sibling" +msgstr "Comme pair" + +#: collect-static/treebeard/treebeard-admin.js:198 +msgid "As child" +msgstr "Comme enfant" diff --git a/mysite/templates/base.html b/mysite/templates/base.html index 7470de5c78aabfa42c2e3708ffd2f115a9f4f902..e881b96884e74317553570bdf6bf223710302559 100644 --- a/mysite/templates/base.html +++ b/mysite/templates/base.html @@ -3,7 +3,7 @@ {%get_current_language as LANGUAGE_CODE%} - Repanier | {% page_attribute "title" %} + {% page_attribute "title" %} @@ -12,6 +12,7 @@ {# #} {# #} {% addtoblock "css" %}{% endaddtoblock %} + {# {% addtoblock "css" %}{% endaddtoblock %}#} {% addtoblock "css" %} {% addtoblock "css" %}{% endaddtoblock %} - - {% addtoblock "css" %}{% endaddtoblock %} + {# {% addtoblock "css" %}{% endaddtoblock %}#} {% addtoblock "css" %} @@ -92,19 +110,14 @@ {% block base_content%}{% endblock %} - diff --git a/mysite/templates/password_reset/recovery_form.html b/mysite/templates/password_reset/recovery_form.html index 80144ae4ec536cdb0734b157f3301d3ab4b61008..81a72b4dd642d7dd1835b233ae15065b077b5d1f 100644 --- a/mysite/templates/password_reset/recovery_form.html +++ b/mysite/templates/password_reset/recovery_form.html @@ -11,7 +11,11 @@
{% csrf_token %}
-

+

+

diff --git a/mysite/templates/password_reset/reset.html b/mysite/templates/password_reset/reset.html index 0506053a84313125b67981e62e80f74684de1a12..7f2c33ea636477fbb3ef0acef0872058bb650d6e 100644 --- a/mysite/templates/password_reset/reset.html +++ b/mysite/templates/password_reset/reset.html @@ -14,12 +14,24 @@ {% blocktrans %}Hi, {{ username }}. Please choose your new password.{% endblocktrans %}
{% csrf_token %} - {{ form.as_p }} +
+ + +
+
+
+ + +
+
{% endif %} - {% endblock %} diff --git a/mysite/templates/registration/login.html b/mysite/templates/registration/login.html index 6e081bac55aabbfb45510c18bb2228c152b5aa85..73c3e3280d0acde480edff698d0b70204c477950 100644 --- a/mysite/templates/registration/login.html +++ b/mysite/templates/registration/login.html @@ -1,5 +1,6 @@ {% extends "base.html" %}{% load i18n admin_static %} {% block base_content %} + {% trans "Too many attempt." as too_many_attempt %}
- {% endblock %} diff --git a/mysite/templates/subpage_base_wo_cms_toolbar.html b/mysite/templates/subpage_base_wo_cms_toolbar.html index 1c15c3d0f745c0c302be86965d7ab46ab332d599..c3745150c0d5a963b7a836a3e453f1d7c9aec7c9 100644 --- a/mysite/templates/subpage_base_wo_cms_toolbar.html +++ b/mysite/templates/subpage_base_wo_cms_toolbar.html @@ -1,8 +1,5 @@ {% extends 'base_wo_cms_toolbar.html' %} {% load cms_tags %} - {% block base_content %} {% block sub_content %}{% endblock %} -{% endblock %} - - +{% endblock %} \ No newline at end of file diff --git a/repanier/__init__.py b/repanier/__init__.py index ab351be8a98e94c0a03a94feb80e17dba0c61246..e1a0f7c157a07847aa2cd3ac623a3a7332211730 100644 --- a/repanier/__init__.py +++ b/repanier/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 from __future__ import unicode_literals -default_app_config = 'repanier.apps.RepanierConfig' \ No newline at end of file +default_app_config = 'repanier.apps.RepanierSettings' diff --git a/repanier/admin.py b/repanier/admin.py index 6f9b49ab640b58ccaae5e2efbafb8761f9c8b7d7..902cba6776658ac749a92929376c811696de5ea9 100644 --- a/repanier/admin.py +++ b/repanier/admin.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 from __future__ import unicode_literals from collections import OrderedDict +from os import sep as os_sep import uuid from django.forms import Textarea from django.utils.timezone import utc import parler +from apps import RepanierSettings from fkey_choice_cache_mixin import ForeignKeyCacheMixin from widget import SelectAdminOrderUnitWidget, PreviewProductOrderWidget @@ -30,8 +32,8 @@ from django_mptt_admin.admin import DjangoMpttAdmin from parler.admin import TranslatableAdmin, TranslatableModelForm -from models import LUT_ProductionMode, OfferItemSend, PurchaseClosedForUpdate, \ - PurchaseOpenedForUpdate, CustomerSend, OfferItemClosed, CustomerInvoice, ProducerInvoice +from models import LUT_ProductionMode, OfferItemSend, PurchaseSendForUpdate, \ + PurchaseOpenedOrClosedForUpdate, CustomerSend, OfferItemClosed, CustomerInvoice, ProducerInvoice from models import Configuration from models import LUT_DepartmentForCustomer from models import LUT_PermanenceRole @@ -67,14 +69,11 @@ from task import task_purchase # LUT class LUTProductionModeAdmin(TranslatableAdmin, DjangoMpttAdmin): - # fields = [ - # ('short_name', 'picture'), - # 'description', - # 'is_active'] list_display = ('short_name', 'is_active') list_display_links = ('short_name',) list_per_page = 17 list_max_show_all = 17 + exclude = ('picture',) admin.site.register(LUT_ProductionMode, LUTProductionModeAdmin) @@ -114,9 +113,6 @@ def create__producer_action(year): def action(modeladmin, request, queryset): queryset = queryset.order_by() return xslx_purchase.admin_export_in(year, queryset) - # user_message = _("Action performed.") - # user_message_level = messages.INFO - # modeladmin.message_user(request, user_message, user_message_level) name = "export_producer_%d" % (year,) return (name, (action, name, _("Export purchases of %s") % (year,))) @@ -183,12 +179,19 @@ class ProducerAdmin(admin.ModelAdmin): list_per_page = 17 list_max_show_all = 17 list_filter = ('is_active', 'invoice_by_basket', 'manage_stock', 'is_resale_price_fixed') - actions = ['export_xlsx', 'import_xlsx', 'recalculate_prices'] + actions = ['export_xlsx_producer_prices', 'export_xlsx_customer_prices', 'import_xlsx', 'recalculate_prices'] - def export_xlsx(self, request, queryset): - return xslx_product.admin_export(request, queryset) + def export_xlsx_producer_prices(self, request, queryset): + return xslx_product.admin_export(request, queryset, producer_prices=True) + + export_xlsx_producer_prices.short_description = _( + "Export products of selected producer(s) as XSLX file at procuder's pices") + + def export_xlsx_customer_prices(self, request, queryset): + return xslx_product.admin_export(request, queryset, producer_prices=False) - export_xlsx.short_description = _("Export products of selected producer(s) as XSLX file") + export_xlsx_customer_prices.short_description = _( + "Export products of selected producer(s) as XSLX file at customer's prices") def import_xlsx(self, request, queryset): return xslx_product.admin_import(self, admin, request, queryset, action='import_xlsx') @@ -225,7 +228,7 @@ class ProducerAdmin(admin.ModelAdmin): return actions def get_list_display(self, request): - if repanier_settings['STOCK']: + if RepanierSettings.stock: return ('short_profile_name', 'get_products', 'get_balance', 'phone1', 'email', 'manage_stock', 'is_active') else: return ('short_profile_name', 'get_products', 'get_balance', 'phone1', 'email', 'is_active') @@ -236,7 +239,7 @@ class ProducerAdmin(admin.ModelAdmin): ('email', 'fax'), ('phone1', 'phone2',) ] - if repanier_settings['DISPLAY_VAT']: + if RepanierSettings.display_vat: fields += [ ('price_list_multiplier', 'producer_price_are_wo_vat', 'is_resale_price_fixed', 'vat_level'), ] @@ -244,7 +247,7 @@ class ProducerAdmin(admin.ModelAdmin): fields += [ ('price_list_multiplier', 'is_resale_price_fixed'), ] - if repanier_settings['STOCK']: + if RepanierSettings.stock: fields += [ ('invoice_by_basket', 'manage_stock',), ] @@ -255,7 +258,7 @@ class ProducerAdmin(admin.ModelAdmin): fields += [ ('address', 'memo'), ] - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: fields += [ ('initial_balance', 'date_balance', 'balance', 'represent_this_buyinggroup'), ] @@ -265,7 +268,7 @@ class ProducerAdmin(admin.ModelAdmin): return fields def get_readonly_fields(self, request, producer=None): - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: if producer is not None: producer_invoice = ProducerInvoice.objects.filter(producer_id=producer.id).order_by().first() if producer_invoice is not None: @@ -275,7 +278,6 @@ class ProducerAdmin(admin.ModelAdmin): return ['represent_this_buyinggroup', 'date_balance', 'balance', 'uuid'] else: return ['uuid',] - return ['represent_this_buyinggroup', 'date_balance', 'balance', 'uuid'] else: return ['represent_this_buyinggroup', 'uuid',] @@ -300,7 +302,6 @@ class UserDataForm(forms.ModelForm): super(UserDataForm, self).__init__(*args, **kwargs) def clean_phone1(self): - # do something that validates your data i = 0 k = 0 phone1 = self.cleaned_data['phone1'] @@ -310,8 +311,6 @@ class UserDataForm(forms.ModelForm): if k == 4: break i += 1 - # print ('----------------------') - # print k if k < 4: self.add_error( 'phone1', @@ -333,7 +332,6 @@ class UserDataForm(forms.ModelForm): if is_customer_form: # Customer username_field_name = 'short_basket_name' - # initial_username = self.fields[username_field_name].initial username = self.cleaned_data.get(username_field_name) user_error1 = _('The given username must be set') user_error2 = _('The given username is used by another user') @@ -448,8 +446,8 @@ class CustomerWithUserDataAdmin(admin.ModelAdmin): ('email', 'email2', 'accept_mails_from_members'), ('phone1', 'phone2', 'accept_phone_call_from_members'), ] - if repanier_settings['INVOICE']: - if repanier_settings['DELIVERY_POINT']: + if RepanierSettings.invoice: + if RepanierSettings.delivery_point: fields += [ ('delivery_point', 'vat_id'), ] @@ -457,15 +455,23 @@ class CustomerWithUserDataAdmin(admin.ModelAdmin): fields += [ ('vat_id',), ] - elif repanier_settings['DELIVERY_POINT']: + elif RepanierSettings.delivery_point: fields += [ ('delivery_point',), ] - fields += [ - ('address', 'city'), - 'memo', - ] - if repanier_settings['INVOICE']: + if customer is not None: + fields += [ + ('address', 'city', 'picture'), + 'memo', + ] + else: + # Do not accept the picture because there is no customer.id for the "upload_to" + fields += [ + ('address', 'city'), + 'memo', + ] + + if RepanierSettings.invoice: if customer is not None: customer_invoice = CustomerInvoice.objects.filter(customer_id=customer.id).order_by().first() if customer_invoice is not None: @@ -488,7 +494,7 @@ class CustomerWithUserDataAdmin(admin.ModelAdmin): return fields def get_readonly_fields(self, request, customer=None): - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: if customer is not None: customer_invoice = CustomerInvoice.objects.filter(customer_id=customer.id).order_by().first() if customer_invoice is not None: @@ -503,24 +509,29 @@ class CustomerWithUserDataAdmin(admin.ModelAdmin): def get_form(self, request, customer=None, **kwargs): form = super(CustomerWithUserDataAdmin, self).get_form(request, customer, **kwargs) - username = form.base_fields['username'] - email = form.base_fields['email'] - if repanier_settings['DELIVERY_POINT']: - delivery_point = form.base_fields["delivery_point"] - delivery_point.empty_label = None - delivery_point.queryset = LUT_DeliveryPoint.objects.filter( + username_field = form.base_fields['username'] + email_field = form.base_fields['email'] + + if RepanierSettings.delivery_point: + delivery_point_field = form.base_fields["delivery_point"] + delivery_point_field.empty_label = None + delivery_point_field.queryset = LUT_DeliveryPoint.objects.filter( rght=F('lft')+1, is_active=True, translations__language_code=translation.get_language() ).order_by('translations__short_name') - delivery_point.widget.can_add_related = False - if customer: + delivery_point_field.widget.can_add_related = False + if customer is not None: user_model = get_user_model() user = user_model.objects.get(id=customer.user_id) - username.initial = getattr(user, user_model.USERNAME_FIELD) - email.initial = user.email + username_field.initial = getattr(user, user_model.USERNAME_FIELD) + email_field.initial = user.email + # One folder by customer to avoid picture names conflicts + picture_field = form.base_fields["picture"] + if hasattr(picture_field.widget, 'upload_to'): + picture_field.widget.upload_to = "customer" + os_sep + str(customer.id) else: # Clean data displayed - username.initial = '' - email.initial = '' + username_field.initial = '' + email_field.initial = '' return form def save_model(self, request, customer, form, change): @@ -556,29 +567,29 @@ class StaffWithUserDataAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): form = super(StaffWithUserDataAdmin, self).get_form(request, obj, **kwargs) - username = form.base_fields['username'] - email = form.base_fields['email'] + username_field = form.base_fields['username'] + email_field = form.base_fields['email'] if "customer_responsible" in form.base_fields: - customer_responsible = form.base_fields["customer_responsible"] - customer_responsible.widget.can_add_related = False + customer_responsible_field = form.base_fields["customer_responsible"] + customer_responsible_field.widget.can_add_related = False if obj: - customer_responsible.empty_label = None - customer_responsible.initial = obj.customer_responsible + customer_responsible_field.empty_label = None + customer_responsible_field.initial = obj.customer_responsible else: - customer_responsible.queryset = Customer.objects.filter(is_active=True).order_by( + customer_responsible_field.queryset = Customer.objects.filter(is_active=True).order_by( "short_basket_name") if obj: user_model = get_user_model() user = user_model.objects.get(id=obj.user_id) - username.initial = getattr(user, user_model.USERNAME_FIELD) + username_field.initial = getattr(user, user_model.USERNAME_FIELD) # username.widget.attrs['readonly'] = True - email.initial = user.email + email_field.initial = user.email else: # Clean data displayed - username.initial = '' + username_field.initial = '' # username.widget.attrs['readonly'] = False - email.initial = '' + email_field.initial = '' return form def save_model(self, request, staff, form, change): @@ -596,7 +607,7 @@ admin.site.register(Staff, StaffWithUserDataAdmin) # Custom Product class ProductDataForm(TranslatableModelForm): - preview_product_order = forms.CharField(label="", widget=PreviewProductOrderWidget) + # preview_product_order = forms.CharField(label="", widget=PreviewProductOrderWidget) def __init__(self, *args, **kwargs): super(ProductDataForm, self).__init__(*args, **kwargs) @@ -673,7 +684,7 @@ class ProductAdmin(TranslatableAdmin): # # Any field in list_editable must also be in list_display. You can’t edit a field that’s not displayed! # You’ll get a validation error if either of these rules are broken. - if repanier_settings['STOCK']: + if RepanierSettings.stock: return ('is_into_offer', 'producer','department_for_customer', 'get_long_name', 'producer_unit_price', 'unit_deposit', 'customer_alert_order_quantity', 'stock', 'is_active') else: @@ -686,10 +697,10 @@ class ProductAdmin(TranslatableAdmin): is_active_value = None is_into_offer_value = None producer_queryset = Producer.objects.none() - current_producer = None + producer = None if product: producer_queryset = Producer.objects.filter(id=product.producer_id).order_by() - current_producer = product.producer + producer = product.producer else: preserved_filters = request.GET.get('_changelist_filters', None) if preserved_filters: @@ -698,7 +709,7 @@ class ProductAdmin(TranslatableAdmin): producer_id = param['producer'] if producer_id: producer_queryset = Producer.objects.filter(id=producer_id).order_by() - current_producer = producer_queryset.first() + producer = producer_queryset.first() if 'department_for_customer' in param: department_for_customer_id = param['department_for_customer'] if 'is_active__exact' in param: @@ -706,11 +717,11 @@ class ProductAdmin(TranslatableAdmin): if 'is_into_offer__exact' in param: is_into_offer_value = param['is_into_offer__exact'] self.fields = [ - ('producer', 'long_name', 'picture'), + ('producer', 'long_name', 'picture2'), # 'preview_product_order', ('order_unit', 'wrapped'), ] - if current_producer is not None and current_producer.is_resale_price_fixed: + if producer is not None and producer.is_resale_price_fixed: self.fields += [ ('producer_unit_price', 'customer_unit_price',), ('unit_deposit', 'order_average_weight',), @@ -725,7 +736,7 @@ class ProductAdmin(TranslatableAdmin): 'production_mode', 'offer_description', ] - if repanier_settings['DISPLAY_VAT']: + if RepanierSettings.display_vat: self.fields += [ ('reference', 'vat_level'), ] @@ -733,64 +744,73 @@ class ProductAdmin(TranslatableAdmin): self.fields += [ ('reference',), ] - if current_producer is not None and current_producer.manage_stock: + if producer is not None and producer.manage_stock: self.fields += [ ('stock', 'limit_order_quantity_to_stock'), ] self.fields += [ ('is_into_offer', 'is_active', 'is_created_on', 'is_updated_on') ] + form = super(ProductAdmin, self).get_form(request, product, **kwargs) - if "producer" in form.base_fields: - producer = form.base_fields["producer"] - department_for_customer = form.base_fields["department_for_customer"] - production_mode = form.base_fields["production_mode"] - - producer.widget.can_add_related = False - department_for_customer.widget.can_add_related = False - production_mode.widget.can_add_related = False - - if product: - producer.empty_label = None - producer.queryset = producer_queryset - department_for_customer.empty_label = None - department_for_customer.queryset = LUT_DepartmentForCustomer.objects.filter( - rght=F('lft')+1, is_active=True, translations__language_code=translation.get_language()).order_by('translations__short_name') - production_mode.empty_label = None + + producer_field = form.base_fields["producer"] + department_for_customer_field = form.base_fields["department_for_customer"] + production_mode_field = form.base_fields["production_mode"] + picture_field = form.base_fields["picture2"] + + producer_field.widget.can_add_related = False + department_for_customer_field.widget.can_add_related = False + production_mode_field.widget.can_add_related = False + # One folder by producer for clarity + if hasattr(picture_field.widget, 'upload_to'): + # picture_field.widget.upload_to += os_sep + str(producer.id) + picture_field.widget.upload_to = "product" + os_sep + str(producer.id) + + if product: + producer_field.empty_label = None + producer_field.queryset = producer_queryset + # department_for_customer_field.empty_label = None + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects.filter( + rght=F('lft') + 1, is_active=True, translations__language_code=translation.get_language()).order_by( + 'translations__short_name') + production_mode_field.empty_label = None + else: + if producer is not None: + if RepanierSettings.display_vat: + vat_level_field = form.base_fields["vat_level"] + vat_level_field.initial = producer.vat_level + producer_field.empty_label = None + producer_field.queryset = producer_queryset + else: + producer_field.choices = [('-1', _("Please select first a producer in the filter of previous screen"))] + if department_for_customer_id is not None: + # department_for_customer_field.empty_label = None + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects.filter( + id=department_for_customer_id + ) else: - if current_producer is not None: - if repanier_settings['DISPLAY_VAT']: - vat_level = form.base_fields["vat_level"] - vat_level.initial = current_producer.vat_level - producer.empty_label = None - producer.queryset = producer_queryset + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects.filter( + rght=F('lft') + 1, is_active=True, translations__language_code=translation.get_language()).order_by( + 'translations__short_name') + if is_active_value: + is_active_field = form.base_fields["is_active"] + if is_active_value == '0': + is_active_field.initial = False else: - producer.choices = [('-1', _("Please select first a producer in the filter of previous screen"))] - if department_for_customer_id: - department_for_customer.empty_label = None - department_for_customer.queryset = LUT_DepartmentForCustomer.objects.filter( - id=department_for_customer_id - ) + is_active_field.initial = True + if is_into_offer_value: + is_into_offer_field = form.base_fields["is_into_offer"] + if is_into_offer_value == '0': + is_into_offer_field.initial = False else: - department_for_customer.queryset = LUT_DepartmentForCustomer.objects.filter( - rght=F('lft')+1, is_active=True, translations__language_code=translation.get_language()).order_by('translations__short_name') - if is_active_value: - is_active = form.base_fields["is_active"] - if is_active_value == '0': - is_active.initial = False - else: - is_active.initial = True - if is_into_offer_value: - is_into_offer = form.base_fields["is_into_offer"] - if is_into_offer_value == '0': - is_into_offer.initial = False - else: - is_into_offer.initial = True - production_mode.queryset = LUT_ProductionMode.objects.filter( - rght=F('lft')+1, is_active=True, translations__language_code=translation.get_language()).order_by('translations__short_name') - if repanier_settings["PRODUCER_PRE_OPENING"]: - order_unit = form.base_fields["order_unit"] - order_unit.choices = LUT_PRODUCER_PRODUCT_ORDER_UNIT + is_into_offer_field.initial = True + production_mode_field.queryset = LUT_ProductionMode.objects.filter( + rght=F('lft') + 1, is_active=True, translations__language_code=translation.get_language()).order_by( + 'translations__short_name') + if RepanierSettings.producer_pre_opening: + order_unit_field = form.base_fields["order_unit"] + order_unit_field.choices = LUT_PRODUCER_PRODUCT_ORDER_UNIT return form def save_model(self, request, product, form, change): @@ -833,7 +853,7 @@ class PermanenceBoardInline(ForeignKeyCacheMixin, admin.TabularInline): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "customer": - kwargs["queryset"] = Customer.objects.filter(is_active=True) # .not_the_buyinggroup() + kwargs["queryset"] = Customer.objects.filter(is_active=True) if db_field.name == "permanence_role": kwargs["queryset"] = LUT_PermanenceRole.objects.filter(is_active=True) return super(PermanenceBoardInline, self).formfield_for_foreignkey(db_field, request, **kwargs) @@ -842,41 +862,6 @@ class PermanenceBoardInline(ForeignKeyCacheMixin, admin.TabularInline): # -> replaced by pre_save signal in model -# class PermanenceDataForm(TranslatableModelForm): -# def __init__(self, *args, **kwargs): -# super(PermanenceDataForm, self).__init__(*args, **kwargs) -# self.user = None -# -# def error(self, field, msg): -# if field not in self._errors: -# self._errors[field] = self.error_class([msg]) -# -# def clean(self): -# cleaned_data = super(PermanenceDataForm, self).clean() -# initial_permanence_date = self.instance.permanence_date -# permanence_date = self.cleaned_data.get("permanence_date") -# short_name = self.cleaned_data.get("short_name") -# if self.instance.id is not None: -# initial_short_name = self.instance.short_name -# else: -# initial_short_name = short_name -# if initial_permanence_date != permanence_date or initial_short_name != short_name: -# if Permanence.objects.filter( -# permanence_date=permanence_date, -# translations__short_name=short_name, -# translations__language_code=translation.get_language() -# ).order_by().exists(): -# self.error('short_name', _( -# 'A permanence with the same distribution date and the same short_name already exist. You must either change te permanence_date or the name.')) -# else: -# # Empty menu cache to eventually display the modified Permanence Label -# menu_pool.clear() -# return cleaned_data -# -# class Meta: -# model = Permanence - - class PermanenceInPreparationAdmin(TranslatableAdmin): # form = PermanenceDataForm fields = ( @@ -1130,7 +1115,7 @@ class PermanenceInPreparationAdmin(TranslatableAdmin): def get_actions(self, request): actions = super(PermanenceInPreparationAdmin, self).get_actions(request) - if not repanier_settings['STOCK']: + if not RepanierSettings.stock: del actions['import_xlsx_stock'] if not actions: @@ -1339,7 +1324,7 @@ class PermanenceDoneAdmin(TranslatableAdmin): actions = super(PermanenceDoneAdmin, self).get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] - if not repanier_settings['INVOICE']: + if not RepanierSettings.invoice: del actions['export_xlsx'] del actions['import_xlsx'] del actions['generate_invoices'] @@ -1411,26 +1396,28 @@ class OfferItemClosedAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): form = super(OfferItemClosedAdmin, self).get_form(request, obj, **kwargs) - permanence = form.base_fields["permanence"] - department_for_customer = form.base_fields["department_for_customer"] - product = form.base_fields["product"] - permanence.widget.can_add_related = False - department_for_customer.widget.can_add_related = False - product.widget.can_add_related = False - permanence.empty_label = None - department_for_customer.empty_label = None - product.empty_label = None + permanence_field = form.base_fields["permanence"] + department_for_customer_field = form.base_fields["department_for_customer"] + product_field = form.base_fields["product"] + + permanence_field.widget.can_add_related = False + department_for_customer_field.widget.can_add_related = False + product_field.widget.can_add_related = False + permanence_field.empty_label = None + department_for_customer_field.empty_label = None + product_field.empty_label = None + if obj is not None: - permanence.queryset = Permanence.objects\ + permanence_field.queryset = Permanence.objects \ .filter(id=obj.permanence_id) - department_for_customer.queryset = LUT_DepartmentForCustomer.objects\ + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects \ .filter(id=obj.department_for_customer_id) - product.queryset = Product.objects\ + product_field.queryset = Product.objects \ .filter(id=obj.product_id) else: - permanence.queryset = Permanence.objects.none() - department_for_customer.queryset = LUT_DepartmentForCustomer.objects.none() - product.queryset = Product.objects.none() + permanence_field.queryset = Permanence.objects.none() + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects.none() + product_field.queryset = Product.objects.none() return form def get_actions(self, request): @@ -1460,19 +1447,18 @@ class OfferItemPurchaseSendForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(OfferItemPurchaseSendForm, self).__init__(*args, **kwargs) - # print "init OfferItemPurchaseSendForm : " + str(self.instance.id) if self.instance.id is not None: self.fields["previous_purchase_price"].initial = self.instance.purchase_price self.fields["previous_selling_price"].initial = self.instance.selling_price class Meta: - model = PurchaseClosedForUpdate + model = PurchaseSendForUpdate fields = "__all__" class OfferItemPurchaseSendInline(ForeignKeyCacheMixin, admin.TabularInline): form = OfferItemPurchaseSendForm - model = PurchaseClosedForUpdate + model = PurchaseSendForUpdate fields = ['customer', 'quantity_invoiced', 'purchase_price', 'comment'] extra = 0 @@ -1589,27 +1575,31 @@ class OfferItemSendAdmin(admin.ModelAdmin): ('producer_unit_price', 'customer_unit_price', 'unit_deposit',), ('offer_purchase_price',) ) + form = super(OfferItemSendAdmin, self).get_form(request, obj, **kwargs) - permanence = form.base_fields["permanence"] - department_for_customer = form.base_fields["department_for_customer"] - product = form.base_fields["product"] - permanence.widget.can_add_related = False - department_for_customer.widget.can_add_related = False - product.widget.can_add_related = False - permanence.empty_label = None - department_for_customer.empty_label = None - product.empty_label = None + + permanence_field = form.base_fields["permanence"] + department_for_customer_field = form.base_fields["department_for_customer"] + product_field = form.base_fields["product"] + + permanence_field.widget.can_add_related = False + department_for_customer_field.widget.can_add_related = False + product_field.widget.can_add_related = False + permanence_field.empty_label = None + department_for_customer_field.empty_label = None + product_field.empty_label = None + if obj is not None: - permanence.queryset = Permanence.objects\ + permanence_field.queryset = Permanence.objects \ .filter(id=obj.permanence_id) - department_for_customer.queryset = LUT_DepartmentForCustomer.objects\ + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects \ .filter(id=obj.department_for_customer_id) - product.queryset = Product.objects\ + product_field.queryset = Product.objects \ .filter(id=obj.product_id) else: - permanence.queryset = Permanence.objects.none() - department_for_customer.queryset = LUT_DepartmentForCustomer.objects.none() - product.queryset = Product.objects.none() + permanence_field.queryset = Permanence.objects.none() + department_for_customer_field.queryset = LUT_DepartmentForCustomer.objects.none() + product_field.queryset = Product.objects.none() return form def get_actions(self, request): @@ -1648,7 +1638,7 @@ class OfferItemSendAdmin(admin.ModelAdmin): is_compensation = True else: is_compensation = False - purchase = purchase_form.instance = PurchaseClosedForUpdate.objects.create( + purchase = purchase_form.instance = PurchaseSendForUpdate.objects.create( permanence_id=offer_item.permanence_id, permanence_date=offer_item.permanence.permanence_date, offer_item_id=offer_item.id, @@ -1723,19 +1713,17 @@ class CustomerPurchaseSendForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(CustomerPurchaseSendForm, self).__init__(*args, **kwargs) - # print "init CustomerPurchaseSendForm : " + str(self.instance.id) if self.instance.id is not None: self.fields["previous_purchase_price"].initial = self.instance.purchase_price class CustomerPurchaseSendInline(ForeignKeyCacheMixin, admin.TabularInline): form = CustomerPurchaseSendForm - model = PurchaseClosedForUpdate + model = PurchaseSendForUpdate fields = ['offer_item', 'quantity_invoiced', 'get_HTML_producer_unit_price', 'get_HTML_unit_deposit', 'purchase_price', 'comment'] - # readonly_fields = ['quantity_invoiced', 'get_HTML_producer_unit_price', 'get_HTML_unit_deposit',] readonly_fields = ['get_HTML_producer_unit_price', 'get_HTML_unit_deposit',] extra = 0 parent_object = None @@ -1748,8 +1736,6 @@ class CustomerPurchaseSendInline(ForeignKeyCacheMixin, admin.TabularInline): .filter(offer_item__translations__language_code=translation.get_language())\ .order_by("offer_item__translations__order_sort_order")\ .distinct() - # .order_by("offer_item__translations__order_sort_order",)\ - # .distinct("id", "translations__order_sort_order") return queryset @@ -1797,26 +1783,29 @@ class CustomerSendAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): form = super(CustomerSendAdmin, self).get_form(request, obj, **kwargs) - permanence = form.base_fields["permanence"] - customer = form.base_fields["customer"] - producer = form.base_fields["producer"] - permanence.widget.can_add_related = False - customer.widget.can_add_related = False - producer.widget.can_add_related = False - permanence.empty_label = None - customer.empty_label = None - producer.empty_label = None + + permanence_field = form.base_fields["permanence"] + customer_field = form.base_fields["customer"] + producer_field = form.base_fields["producer"] + + permanence_field.widget.can_add_related = False + customer_field.widget.can_add_related = False + producer_field.widget.can_add_related = False + permanence_field.empty_label = None + customer_field.empty_label = None + producer_field.empty_label = None + if obj is not None: - permanence.queryset = Permanence.objects\ + permanence_field.queryset = Permanence.objects \ .filter(id=obj.permanence_id) - customer.queryset = Customer.objects\ + customer_field.queryset = Customer.objects \ .filter(id=obj.customer_id) - producer.queryset = Producer.objects\ + producer_field.queryset = Producer.objects \ .filter(id=obj.producer_id) else: - permanence.queryset = Permanence.objects.none() - customer.queryset = Customer.objects.none() - producer.queryset = Producer.objects.none() + permanence_field.queryset = Permanence.objects.none() + customer_field.queryset = Customer.objects.none() + producer_field.queryset = Producer.objects.none() return form def has_add_permission(self, request): @@ -1843,7 +1832,6 @@ class CustomerSendAdmin(admin.ModelAdmin): request, customer_producer_invoice, form, change) def save_related(self, request, form, formsets, change): - # print "----------------------------------------- save_related CustomerSendAdmin : " + str(form.instance.id) for formset in formsets: # option.py -> construct_change_message doesn't test the presence of those array not created at form initialisation... if not hasattr(formset, 'new_objects'): formset.new_objects = [] @@ -1855,8 +1843,6 @@ class CustomerSendAdmin(admin.ModelAdmin): formset = formsets[0] for purchase_form in formset: purchase=purchase_form.instance - # print("-----------------------") - # print dir(purchase_form.instance) if purchase.id is None: offer_item = purchase.offer_item if offer_item.product.vat_level in [VAT_200, VAT_300] \ @@ -1865,7 +1851,7 @@ class CustomerSendAdmin(admin.ModelAdmin): is_compensation = True else: is_compensation = False - purchase = purchase_form.instance = PurchaseClosedForUpdate.objects.create( + purchase = purchase_form.instance = PurchaseSendForUpdate.objects.create( permanence_id=offer_item.permanence_id, permanence_date=offer_item.permanence.permanence_date, offer_item_id=offer_item.id, @@ -1933,11 +1919,29 @@ class PurchaseWithProductForm(forms.ModelForm): purchase = self.instance self.fields["quantity_invoiced"].initial = purchase.quantity_invoiced + def clean_product(self): + product_id = sint(self.cleaned_data.get("product")) + if product_id < 0: + self.add_error( + 'product', + _("Please select first a producer in the filter of previous screen") + ) + else: + permanence = self.cleaned_data.get("permanence") + customer = self.cleaned_data.get("customer") + purchase = Purchase.objects.filter( + permanence_id=permanence.id, customer_id=customer.id, + offer_item__product_id=product_id, offer_item__permanence_id=permanence.id + ).order_by().only("id").first() + if purchase is not None and self.instance is not None: + self.instance.id = purchase.id + return product_id + def clean(self): cleaned_data = super(PurchaseWithProductForm, self).clean() # self._validate_unique = False is required to avoid # django.db.models.fields.FieldDoesNotExist: - # PurchaseOpened has no field named 'product' + # PurchaseOpenedOrClosed has no field named 'product' self._validate_unique = False return cleaned_data @@ -1974,10 +1978,10 @@ class PurchaseWithProductAdmin(admin.ModelAdmin): def __init__(self, model, admin_site): super(PurchaseWithProductAdmin, self).__init__(model, admin_site) - self.q_previous_order = 0 + self.q_previous_order = DECIMAL_ZERO def get_department_for_customer(self, obj): - return obj.offer_item.department_for_customer.short_name + return obj.offer_item.department_for_customer get_department_for_customer.short_description = _("department_for_customer") @@ -2009,46 +2013,47 @@ class PurchaseWithProductAdmin(admin.ModelAdmin): if 'producer' in param: producer_id = param['producer'] if "permanence" in form.base_fields: - permanence = form.base_fields["permanence"] - customer = form.base_fields["customer"] - product = form.base_fields["product"] - permanence.widget.can_add_related = False - customer.widget.can_add_related = False - product.widget.can_add_related = False + permanence_field = form.base_fields["permanence"] + customer_field = form.base_fields["customer"] + product_field = form.base_fields["product"] + permanence_field.widget.can_add_related = False + customer_field.widget.can_add_related = False + product_field.widget.can_add_related = False if obj is not None: - self.q_previous_order = obj.get_quantity - permanence.empty_label = None - permanence.queryset = Permanence.objects\ + self.q_previous_order = obj.get_quantity() + permanence_field.empty_label = None + permanence_field.queryset = Permanence.objects \ .filter(id=obj.permanence_id) - customer.empty_label = None - customer.queryset = Customer.objects\ + customer_field.empty_label = None + customer_field.queryset = Customer.objects \ .filter(id=obj.customer_id) - product.empty_label = None - product.choices = [(o.id, str(o)) for o in Product.objects.filter(offeritem=obj.offer_item_id, + product_field.empty_label = None + product_field.choices = [(o.id, str(o)) for o in Product.objects.filter(offeritem=obj.offer_item_id, translations__language_code=translation.get_language() ).order_by('translations__long_name')] else: self.q_previous_order = 0 if permanence_id is not None: - permanence.empty_label = None - permanence.queryset = Permanence.objects\ + permanence_field.empty_label = None + permanence_field.queryset = Permanence.objects \ .filter(id=permanence_id) else: - permanence.queryset = Permanence.objects\ + permanence_field.queryset = Permanence.objects \ .filter(status__in=self.permanence_status_list) if producer_id is not None: - product.choices = [ (o.id, str(o)) for o in Product.objects + product_field.choices = [(o.id, str(o)) for o in Product.objects .filter(is_active=True,producer_id=producer_id, translations__language_code=translation.get_language() ).order_by('translations__long_name')] else: - product.choices = [('-1', _("Please select first a producer in the filter of previous screen"))] + product_field.choices = [ + ('-1', _("Please select first a producer in the filter of previous screen"))] if customer_id is not None: - customer.empty_label = None - customer.queryset = Customer.objects.filter(id=customer_id, is_active=True, may_order=True) + customer_field.empty_label = None + customer_field.queryset = Customer.objects.filter(id=customer_id, is_active=True, may_order=True) else: - customer.queryset = Customer.objects.filter(is_active=True, may_order=True) + customer_field.queryset = Customer.objects.filter(is_active=True, may_order=True) return form @transaction.atomic @@ -2100,7 +2105,7 @@ class PurchaseWithProductAdmin(admin.ModelAdmin): return actions -class PurchaseOpenedAdmin(PurchaseWithProductAdmin): +class PurchaseOpenedOrClosedAdmin(PurchaseWithProductAdmin): fields = ( 'permanence', 'customer', @@ -2111,13 +2116,13 @@ class PurchaseOpenedAdmin(PurchaseWithProductAdmin): @property def permanence_status_list(self): - return [PERMANENCE_OPENED,] + return [PERMANENCE_OPENED, PERMANENCE_CLOSED] -admin.site.register(PurchaseOpenedForUpdate, PurchaseOpenedAdmin) +admin.site.register(PurchaseOpenedOrClosedForUpdate, PurchaseOpenedOrClosedAdmin) -class PurchaseClosedAdmin(PurchaseWithProductAdmin): +class PurchaseSendAdmin(PurchaseWithProductAdmin): fields = ( 'permanence', 'customer', @@ -2130,9 +2135,10 @@ class PurchaseClosedAdmin(PurchaseWithProductAdmin): @property def permanence_status_list(self): - return [PERMANENCE_CLOSED, PERMANENCE_SEND] + return [PERMANENCE_SEND] + -admin.site.register(PurchaseClosedForUpdate, PurchaseClosedAdmin) +admin.site.register(PurchaseSendForUpdate, PurchaseSendAdmin) # Accounting @@ -2210,25 +2216,26 @@ class BankAccountAdmin(admin.ModelAdmin): form = super(BankAccountAdmin, self).get_form(request, obj, **kwargs) if obj: if obj.customer: - customer = form.base_fields["customer"] - customer.widget.can_add_related = False - customer.empty_label = None - customer.queryset = Customer.objects.filter(id=obj.customer_id) + customer_field = form.base_fields["customer"] + customer_field.widget.can_add_related = False + customer_field.empty_label = None + customer_field.queryset = Customer.objects.filter(id=obj.customer_id) if obj.producer: - producer = form.base_fields["producer"] - producer.widget.can_add_related = False - producer.empty_label = None - producer.queryset = Producer.objects.filter(id=obj.producer_id) + producer_field = form.base_fields["producer"] + producer_field.widget.can_add_related = False + producer_field.empty_label = None + producer_field.queryset = Producer.objects.filter(id=obj.producer_id) else: - producer = form.base_fields["producer"] - customer = form.base_fields["customer"] - producer.widget.can_add_related = False - customer.widget.can_add_related = False - producer.queryset = Producer.objects.filter(represent_this_buyinggroup=False, is_active=True).order_by( + producer_field = form.base_fields["producer"] + customer_field = form.base_fields["customer"] + producer_field.widget.can_add_related = False + customer_field.widget.can_add_related = False + producer_field.queryset = Producer.objects.filter(represent_this_buyinggroup=False, + is_active=True).order_by( "short_profile_name") # customer.queryset = Customer.objects.filter(represent_this_buyinggroup=False, is_active=True).order_by( # "short_basket_name") - customer.queryset = Customer.objects.filter(is_active=True).order_by( + customer_field.queryset = Customer.objects.filter(is_active=True).order_by( "short_basket_name") return form diff --git a/repanier/apps.py b/repanier/apps.py index bbbcf4b4e138bb934217ded1152a30a4baf07257..90a5dde3d8c009e74eaa9957b24320fd9d9c6c43 100644 --- a/repanier/apps.py +++ b/repanier/apps.py @@ -4,41 +4,71 @@ from django.apps import AppConfig from django.conf import settings from django.contrib.sites.models import Site from django.utils.translation import ugettext_lazy as _ +from callback import pasword_reset_callback from const import * +from password_reset.signals import user_recovers_password -repanier_settings = { - 'CONFIG': None, - 'TEST_MODE': None, - 'GROUP_NAME': None, - 'PERMANENCE_NAME': None, - 'PERMANENCES_NAME': None, - 'PERMANENCE_ON_NAME': None, - 'MAX_WEEK_WO_PARTICIPATION': None, - 'SEND_OPENING_MAIL_TO_CUSTOMER': None, - 'SEND_ORDER_MAIL_TO_CUSTOMER': None, - 'SEND_ORDER_MAIL_TO_PRODUCER': None, - 'SEND_ORDER_MAIL_TO_BOARD': None, - 'SEND_INVOICE_MAIL_TO_CUSTOMER': None, - 'SEND_INVOICE_MAIL_TO_PRODUCER': None, - 'DISPLAY_ANONYMOUS_ORDER_FORM': None, - 'DISPLAY_PRODUCERS_ON_ORDER_FORM': None, - 'BANK_ACCOUNT': None, - 'PRODUCER_ORDER_ROUNDED': None, - 'PRODUCER_PRE_OPENING': None, - 'ACCEPT_CHILD_GROUP': None, - 'DELIVERY_POINT': None, - 'INVOICE': None, - 'STOCK': None, - 'DISPLAY_VAT': None, - 'VAT_ID': None, - 'PAGE_BREAK_ON_CUSTOMER_CHECK': None -} +# repanier_settings = { +# 'CONFIG': None, +# 'TEST_MODE': None, +# 'GROUP_NAME': None, +# 'PERMANENCE_NAME': None, +# 'PERMANENCES_NAME': None, +# 'PERMANENCE_ON_NAME': None, +# 'MAX_WEEK_WO_PARTICIPATION': None, +# 'SEND_OPENING_MAIL_TO_CUSTOMER': None, +# 'SEND_ORDER_MAIL_TO_CUSTOMER': None, +# 'SEND_ORDER_MAIL_TO_PRODUCER': None, +# 'SEND_ORDER_MAIL_TO_BOARD': None, +# 'SEND_INVOICE_MAIL_TO_CUSTOMER': None, +# 'SEND_INVOICE_MAIL_TO_PRODUCER': None, +# 'DISPLAY_ANONYMOUS_ORDER_FORM': None, +# 'DISPLAY_PRODUCERS_ON_ORDER_FORM': None, +# 'BANK_ACCOUNT': None, +# 'PRODUCER_ORDER_ROUNDED': None, +# 'PRODUCER_PRE_OPENING': None, +# 'ACCEPT_CHILD_GROUP': None, +# 'DELIVERY_POINT': None, +# 'INVOICE': None, +# 'STOCK': None, +# 'DISPLAY_VAT': None, +# 'VAT_ID': None, +# 'PAGE_BREAK_ON_CUSTOMER_CHECK': None +# } +# class RepanierConfig(AppConfig): -class RepanierConfig(AppConfig): +class RepanierSettings(AppConfig): name = 'repanier' verbose_name = "Repanier" + config = None + test_mode = None + group_name = None + permanence_name = None + permanences_name = None + permanence_on_name = None + max_week_wo_participation = None + send_opening_mail_to_customer = None + send_order_mail_to_customer = None + send_order_mail_to_producer = None + send_order_mail_to_board = None + send_invoice_mail_to_customer = None + send_invoice_mail_to_producer = None + invoice = None + stock = None + display_anonymous_order_form = None + display_producer_on_order_form = None + bank_account = None + producer_order_rounded = None + producer_pre_opening = None + accept_child_group = None + delivery_point = None + display_vat = None + vat_id = None + page_break_on_customer_check = None + def ready(self): + user_recovers_password.connect(pasword_reset_callback) from models import Configuration try: config = Configuration.objects.filter(id=DECIMAL_ONE).first() diff --git a/repanier/auth_backend.py b/repanier/auth_backend.py index 7d24f9931e897214d0bee5e159dd076e571793cb..40801f1bcd3c86de143cbba6f06950eb8d63e6b1 100644 --- a/repanier/auth_backend.py +++ b/repanier/auth_backend.py @@ -4,7 +4,7 @@ from django import forms from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User -from django.db.models import F +from django.db.models import F, Q from const import DECIMAL_ZERO, DECIMAL_ONE, DECIMAL_TWO from email.email_alert import send_error from models import Customer, Staff, Configuration @@ -21,107 +21,100 @@ class RepanierCustomBackend(ModelBackend): def authenticate(self, username=None, password=None, confirm=None, **kwargs): self.user = None - # try: - user_username = User.objects.filter(username=username[:30]).order_by().first() - if user_username is None: - user_username = User.objects.filter(email=username).order_by().first() - if user_username is not None: - username = user_username.username + # try: (Q(income__gte=5000) | Q(income__isnull=True)) + user_username = User.objects.filter(Q(username=username[:30]) | Q(email=username)).order_by().first() if user_username is not None: + username = user_username.username staff = Staff.objects.filter( user=user_username, is_active=True ).order_by().first() - else: - staff = None - if staff is not None: - customer = staff.customer_responsible - else: - customer = Customer.objects.filter( - user=user_username, is_active=True - ).order_by().first() - user_or_none = super(RepanierCustomBackend, self).authenticate(username, password) - if user_or_none is not None: - if user_or_none.is_superuser and customer is not None: - # a customer or a staff member may not be superuser - user_or_none = None - if customer is not None: - # This is a customer or staff member - login_attempt_counter = customer.login_attempt_counter - if login_attempt_counter > DECIMAL_ONE: - if confirm is None: - confirm = "" - else: - confirm = str(sint(confirm)) - phone_digits = "" - i = 0 - phone1 = customer.phone1 - while i < len(phone1): - if '0' <= phone1[i] <= '9': - phone_digits += phone1[i] - i += 1 - if confirm is not None and len(confirm) >= 4 and phone_digits.endswith(confirm): - pass - else: - # Sorry you may no log in because the four last digits of your phone are not given - user_or_none = None - if user_or_none is None: - if login_attempt_counter < 50: - Customer.objects.filter(id=customer.id).update( - login_attempt_counter=F('login_attempt_counter') + - DECIMAL_ONE - ) - if login_attempt_counter > DECIMAL_ONE: - raise forms.ValidationError( - _("Too many attempt."), - code='attempt', - ) + if staff is not None: + customer = staff.customer_responsible else: - if login_attempt_counter < 10: - Customer.objects.filter(id=customer.id).update( - login_attempt_counter=DECIMAL_ZERO - ) + customer = Customer.objects.filter( + user=user_username, is_active=True + ).order_by().first() + user_or_none = super(RepanierCustomBackend, self).authenticate(username, password) + if customer is not None: + # This is a customer or staff member + login_attempt_counter = customer.login_attempt_counter + if login_attempt_counter > DECIMAL_TWO: + # if confirm is None: + # confirm = "" + # else: + # confirm = str(sint(confirm)) + phone_digits = "" + i = 0 + phone1 = customer.phone1 + while i < len(phone1): + if '0' <= phone1[i] <= '9': + phone_digits += phone1[i] + i += 1 + if confirm is not None and len(confirm) >= 4 and phone_digits.endswith(confirm): + pass + else: + # Sorry you may no log in because the four last digits of your phone are not given + user_or_none = None + if user_or_none is None: + if login_attempt_counter < 20: + Customer.objects.filter(id=customer.id).update( + login_attempt_counter=F('login_attempt_counter') + + DECIMAL_ONE + ) + else: + # That's really not normal + Customer.objects.filter(id=customer.id).update( + may_order=False + ) + Staff.objects.filter(customer_responsible_id=customer.id).update( + is_active=False + ) + if login_attempt_counter > DECIMAL_ONE: + raise forms.ValidationError( + _("Too many attempt."), + code='attempt', + ) else: Customer.objects.filter(id=customer.id).update( - may_order=False - ) - Staff.objects.filter(customer_responsible_id=customer.id).update( - is_active=False - ) - elif user_username is not None and user_username.is_superuser: - # This is the superuser. One and only one superuser should be defined. - login_attempt_counter = Configuration.objects.filter( - id=DECIMAL_ONE - ).only( - 'login_attempt_counter' - ).first().login_attempt_counter - if user_or_none is None: - # Failed to log in - if login_attempt_counter < 50: - Configuration.objects.filter(id=DECIMAL_ONE).update( - login_attempt_counter=F('login_attempt_counter') + - DECIMAL_ONE - ) - if login_attempt_counter > DECIMAL_ONE: - send_error("Login attempt failed : %s" % username) - else: - # Log in successful - if login_attempt_counter > 6: - # Sorry you may no log in because of too many failed log in attempt - user_or_none = None - else: - Configuration.objects.filter(id=DECIMAL_ONE).update( login_attempt_counter=DECIMAL_ZERO ) - if login_attempt_counter > DECIMAL_TWO: - send_error("Login attempt success : %s" % username) - # except: - # user_or_none = None - self.user = user_or_none - # if user_or_none : - # print ('Authenticate user : %s' % getattr(user_or_none, get_user_model().USERNAME_FIELD)) - # else: - # print ('Authenticate user : not defined') - return user_or_none + elif user_username is not None and user_username.is_superuser: + # This is the superuser. One and only one superuser should be defined. + login_attempt_counter = Configuration.objects.filter( + id=DECIMAL_ONE + ).only( + 'login_attempt_counter' + ).first().login_attempt_counter + if user_or_none is None: + # Failed to log in + if login_attempt_counter < 50: + Configuration.objects.filter(id=DECIMAL_ONE).update( + login_attempt_counter=F('login_attempt_counter') + + DECIMAL_ONE + ) + if login_attempt_counter > DECIMAL_ONE: + send_error("Login attempt failed : %s" % username) + else: + # Log in successful + if login_attempt_counter > 6: + # Sorry you may no log in because of too many failed log in attempt + user_or_none = None + else: + Configuration.objects.filter(id=DECIMAL_ONE).update( + login_attempt_counter=DECIMAL_ZERO + ) + if login_attempt_counter > DECIMAL_TWO: + send_error("Login attempt success : %s" % username) + # except: + # user_or_none = None + self.user = user_or_none + # if user_or_none : + # print ('Authenticate user : %s' % getattr(user_or_none, get_user_model().USERNAME_FIELD)) + # else: + # print ('Authenticate user : not defined') + return user_or_none + else: + return None def get_user(self, user_id): if self.user is not None and self.user.id == user_id: diff --git a/repanier/callback.py b/repanier/callback.py new file mode 100644 index 0000000000000000000000000000000000000000..5385a31168be76e55516384eaaa2201fa1b90d7f --- /dev/null +++ b/repanier/callback.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 +from __future__ import unicode_literals +from const import DECIMAL_ZERO + + +def pasword_reset_callback(sender, user, request, **kwargs): + if not user.is_superuser: + from models import Staff, Customer + + staff = Staff.objects.filter( + user=user, is_active=True + ).order_by().first() + if staff is not None: + customer = staff.customer_responsible + else: + customer = Customer.objects.filter( + user=user, is_active=True + ).order_by().first() + Customer.objects.filter(id=customer.id).update( + login_attempt_counter=DECIMAL_ZERO + ) diff --git a/repanier/cms_toolbar.py b/repanier/cms_toolbar.py index 95ab625d11eb2e577d8cd1bfbfc49b2b45a7ed0a..e024b2fe9a211c9896953ee28fae32bdf67352ba 100644 --- a/repanier/cms_toolbar.py +++ b/repanier/cms_toolbar.py @@ -6,7 +6,7 @@ from cms.toolbar_pool import toolbar_pool from cms.toolbar.items import Break, SubMenu from cms.cms_toolbar import ADMIN_MENU_IDENTIFIER, ADMINISTRATION_BREAK from cms.toolbar_base import CMSToolbar -from apps import repanier_settings +from apps import RepanierSettings from const import * from models import Configuration @@ -39,7 +39,7 @@ class RepanierToolbar(CMSToolbar): office_menu.add_sideframe_item(_('Permanence Role List'), url=url) url = reverse('admin:repanier_lut_productionmode_changelist') office_menu.add_sideframe_item(_('Production Mode List'), url=url) - if repanier_settings['DELIVERY_POINT']: + if RepanierSettings.delivery_point: url = reverse('admin:repanier_lut_deliverypoint_changelist') office_menu.add_sideframe_item(_('Delivery Point List'), url=url) url = reverse('admin:repanier_lut_departmentforcustomer_changelist') @@ -55,12 +55,14 @@ class RepanierToolbar(CMSToolbar): position += 1 url = reverse('admin:repanier_permanenceinpreparation_changelist') - admin_menu.add_sideframe_item(_("%(name)s in preparation list") % {'name': repanier_settings['PERMANENCES_NAME']}, url=url, position=position) + admin_menu.add_sideframe_item(_("%(name)s in preparation list") % {'name': RepanierSettings.permanences_name}, + url=url, position=position) - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: position += 1 url = reverse('admin:repanier_permanencedone_changelist') - admin_menu.add_sideframe_item(_("%(name)s done list") % {'name': repanier_settings['PERMANENCES_NAME']}, url=url, position=position) + admin_menu.add_sideframe_item(_("%(name)s done list") % {'name': RepanierSettings.permanences_name}, + url=url, position=position) position += 1 url = reverse('admin:repanier_bankaccount_changelist') @@ -68,5 +70,5 @@ class RepanierToolbar(CMSToolbar): else: position += 1 url = reverse('admin:repanier_permanencedone_changelist') - admin_menu.add_sideframe_item(_("%(name)s archived list") % {'name': repanier_settings['PERMANENCES_NAME']}, url=url, position=position) - + admin_menu.add_sideframe_item(_("%(name)s archived list") % {'name': RepanierSettings.permanences_name}, + url=url, position=position) diff --git a/repanier/email/email_alert.py b/repanier/email/email_alert.py index 7dbcb88cc202a37136042cd5694646ae3de58c93..b0b8879f43dfb4bce97711a0e6d7ec7c9e7fa68c 100644 --- a/repanier/email/email_alert.py +++ b/repanier/email/email_alert.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 from __future__ import unicode_literals -from repanier.apps import repanier_settings +from repanier.apps import RepanierSettings from repanier.const import * from django.conf import settings from django.core.mail import send_mail @@ -9,7 +9,7 @@ from django.core.mail import send_mail def send(permanence): try: - send_mail("Alert - %s - %s" % (permanence, repanier_settings['GROUP_NAME']), permanence.get_status_display(), + send_mail("Alert - %s - %s" % (permanence, RepanierSettings.group_name), permanence.get_status_display(), "%s@repanier.be" % (settings.ALLOWED_HOSTS[0]), [v for k, v in settings.ADMINS]) except: pass @@ -17,7 +17,7 @@ def send(permanence): def send_error(error_str): try: - send_mail("Alert - %s" % repanier_settings['GROUP_NAME'], error_str, + send_mail("Alert - %s" % RepanierSettings.group_name, error_str, "%s@repanier.be" % (settings.ALLOWED_HOSTS[0]), [v for k, v in settings.ADMINS]) except: pass diff --git a/repanier/email/email_invoice.py b/repanier/email/email_invoice.py index d1f2e44a716af740722c2092ffbc463c83137ba4..9783bb482013038609971801a2f759a202196695 100644 --- a/repanier/email/email_invoice.py +++ b/repanier/email/email_invoice.py @@ -8,6 +8,7 @@ from django.utils.html import strip_tags from django.utils.translation import ugettext_lazy as _ from parler.models import TranslationDoesNotExist from openpyxl.writer.excel import save_virtual_workbook +from repanier.apps import RepanierSettings from repanier.models import Customer from repanier.models import Permanence from repanier.models import Producer @@ -22,7 +23,7 @@ def send(permanence_id): permanence = Permanence.objects.get(id=permanence_id) sender_email, sender_function, signature, cc_email_staff = get_signature(is_reply_to_invoice_email=True) - if repanier_settings['SEND_INVOICE_MAIL_TO_PRODUCER']: + if RepanierSettings.send_invoice_mail_to_producer: # To the producer we speak of "payment". # This is the detail of the paiment to the producer, i.e. received products filename = ("%s - %s.xlsx" % (_("Payment"), permanence)).encode('ascii', errors='replace').replace('?', '_') @@ -33,16 +34,18 @@ def send(permanence_id): long_profile_name = producer.long_profile_name if producer.long_profile_name is not None else producer.short_profile_name wb = xslx_invoice.export(permanence=permanence, producer=producer, sheet_name=long_profile_name) if wb is not None: - invoice_producer_mail = repanier_settings['CONFIG'].invoice_producer_mail + invoice_producer_mail = RepanierSettings.config.invoice_producer_mail template = Template(invoice_producer_mail) context = djangoContext({ 'long_profile_name' : long_profile_name, 'permanence': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('producer_invoice_uuid_view', args=(0, producer.uuid)), permanence)), - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe( + '%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) email = EmailMultiAlternatives( - "%s - %s - %s - %s" % (_('Payment'), permanence, repanier_settings['GROUP_NAME'], long_profile_name), + "%s - %s - %s - %s" % ( + _('Payment'), permanence, RepanierSettings.group_name, long_profile_name), strip_tags(html_content), sender_email, [producer.email], @@ -54,7 +57,7 @@ def send(permanence_id): email.attach_alternative(html_content, "text/html") send_email(email=email) - if repanier_settings['SEND_INVOICE_MAIL_TO_CUSTOMER']: + if RepanierSettings.send_invoice_mail_to_customer: # To the customer we speak of "invoice". # This is the detail of the invoice, i.e. sold products filename = ("%s - %s.xlsx" % (_("Invoice"), permanence)).encode('ascii', errors='replace').replace('?', '_') @@ -71,17 +74,18 @@ def send(permanence_id): email_customer = [customer.user.email,] if customer.email2 is not None and len(customer.email2) > 0: email_customer.append(customer.email2) - invoice_customer_mail = repanier_settings['CONFIG'].invoice_customer_mail + invoice_customer_mail = RepanierSettings.config.invoice_customer_mail template = Template(invoice_customer_mail) context = djangoContext({ 'long_basket_name' : long_basket_name, 'permanence': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('producer_invoice_uuid_view', args=(0, producer.uuid)), permanence)), 'invoice_description' : invoice_description, - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe( + '%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) email = EmailMultiAlternatives( - "%s - %s - %s - %s" % (_('Invoice'), permanence, repanier_settings['GROUP_NAME'], long_basket_name), + "%s - %s - %s - %s" % (_('Invoice'), permanence, RepanierSettings.group_name, long_basket_name), strip_tags(html_content), sender_email, email_customer, diff --git a/repanier/email/email_offer.py b/repanier/email/email_offer.py index eb8c4e3000b57e7c9f3b10ff3bc73dd8b4a93fc0..63fd2a276634a768aa470a062539fdb9eea0ee4a 100644 --- a/repanier/email/email_offer.py +++ b/repanier/email/email_offer.py @@ -16,6 +16,7 @@ from django.utils.html import strip_tags # from django.utils import translation from django.utils.translation import ugettext_lazy as _ from parler.models import TranslationDoesNotExist +from repanier.apps import RepanierSettings from repanier.models import Permanence, Producer from repanier.models import Staff from repanier.models import Customer @@ -23,9 +24,9 @@ from repanier.tools import * def send_pre_opening(permanence_id): - if repanier_settings['SEND_OPENING_MAIL_TO_CUSTOMER']: + if RepanierSettings.producer_pre_opening: translation.activate(settings.LANGUAGE_CODE) - offer_producer_mail = repanier_settings['CONFIG'].offer_producer_mail + offer_producer_mail = RepanierSettings.config.offer_producer_mail permanence = Permanence.objects.get(id=permanence_id) sender_email, sender_function, signature, cc_email_staff = get_signature(is_reply_to_order_email=True) try: @@ -46,11 +47,12 @@ def send_pre_opening(permanence_id): 'permanence': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('pre_order_uuid_view', args=(0, producer.offer_uuid)), _("offer"))), 'offer_description': mark_safe(offer_description), 'offer': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('pre_order_uuid_view', args=(0, producer.offer_uuid)), _("offer"))), - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe( + '%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) email = EmailMultiAlternatives( - "%s - %s - %s" % (_("Pre-opening of orders"), permanence, repanier_settings['GROUP_NAME']), + "%s - %s - %s" % (_("Pre-opening of orders"), permanence, RepanierSettings.group_name), strip_tags(html_content), sender_email, [producer.email], @@ -61,7 +63,7 @@ def send_pre_opening(permanence_id): def send(permanence_id): - if repanier_settings['SEND_OPENING_MAIL_TO_CUSTOMER']: + if RepanierSettings.send_opening_mail_to_customer: translation.activate(settings.LANGUAGE_CODE) permanence = Permanence.objects.get(id=permanence_id) sender_email, sender_function, signature, cc_email_staff = get_signature(is_reply_to_order_email=True) @@ -74,12 +76,12 @@ def send(permanence_id): offer_description = permanence.offer_description except TranslationDoesNotExist: offer_description = "" - offer_customer_mail = repanier_settings['CONFIG'].offer_customer_mail + offer_customer_mail = RepanierSettings.config.offer_customer_mail template = Template(offer_customer_mail) context = djangoContext({ 'permanence': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('order_view', args=(permanence.id,)), permanence)), 'offer_description': mark_safe(offer_description), - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) # import sys @@ -89,7 +91,7 @@ def send(permanence_id): # print "%s" % html_content # i = 1 / 0 email = EmailMultiAlternatives( - "%s - %s - %s" % (_("Opening of orders"), permanence, repanier_settings['GROUP_NAME']), + "%s - %s - %s" % (_("Opening of orders"), permanence, RepanierSettings.group_name), strip_tags(html_content), sender_email, bcc=cc_email_staff diff --git a/repanier/email/email_order.py b/repanier/email/email_order.py index 08d3e1cd835545a180aebf472bab826c118f864d..c009c85a76d44803298f29ddbcd304ae248e2569 100644 --- a/repanier/email/email_order.py +++ b/repanier/email/email_order.py @@ -12,6 +12,7 @@ from django.core.mail import EmailMultiAlternatives from django.utils.html import strip_tags from django.utils.translation import ugettext_lazy as _ from openpyxl.writer.excel import save_virtual_workbook +from repanier.apps import RepanierSettings from repanier.models import Customer, CustomerInvoice from repanier.models import Permanence from repanier.models import PermanenceBoard @@ -46,7 +47,7 @@ def send(permanence_id): board_composition_and_description += "%s%s
" % (member, r.description) # Orders send to our producers - if repanier_settings['SEND_ORDER_MAIL_TO_PRODUCER']: + if RepanierSettings.send_order_mail_to_producer: producer_set = Producer.objects.filter( permanence=permanence_id).order_by() for producer in producer_set: @@ -65,19 +66,20 @@ def send(permanence_id): wb = xslx_order.export_producer_by_customer(permanence=permanence, producer=producer, wb=wb) else: duplicate = False - order_producer_mail = repanier_settings['CONFIG'].order_producer_mail + order_producer_mail = RepanierSettings.config.order_producer_mail template = Template(order_producer_mail) context = djangoContext({ 'long_profile_name': long_profile_name, 'order_empty': order_empty, 'duplicate': duplicate, 'permanence': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('order_view', args=(permanence.id,)), permanence)), - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe( + '%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) email = EmailMultiAlternatives( - "%s - %s - %s - %s" % (_('Order'), permanence, repanier_settings['GROUP_NAME'], long_profile_name), + "%s - %s - %s - %s" % (_('Order'), permanence, RepanierSettings.group_name, long_profile_name), strip_tags(html_content), sender_email, [producer.email], @@ -93,7 +95,7 @@ def send(permanence_id): send_email(email=email) # Orders send to our customers - if repanier_settings['SEND_ORDER_MAIL_TO_CUSTOMER']: + if RepanierSettings.send_order_mail_to_customer: customer_set = Customer.objects.filter( purchase__permanence=permanence_id, represent_this_buyinggroup=False).order_by().distinct() for customer in customer_set: @@ -106,14 +108,17 @@ def send(permanence_id): email_customer = [customer.user.email,] if customer.email2 is not None and len(customer.email2) > 0: email_customer.append(customer.email2) - if repanier_settings['INVOICE'] is not None: + if RepanierSettings.invoice is not None: customer_last_balance = "%s %s %s %s €" % (_('The balance of your account as of'), customer.date_balance.strftime('%d-%m-%Y'), _('is'), number_format(customer.balance, 2)) - if repanier_settings['BANK_ACCOUNT'] is not None: + if RepanierSettings.bank_account is not None: if (order_amount - customer.balance) > 0: customer_payment_needed = "%s %s € %s %s %s \"%s, %s\"" % (_('Please pay'), number_format(order_amount - customer.balance, 2), _('to the bank account number'), - repanier_settings['BANK_ACCOUNT'], _('with communication'), customer.short_basket_name, permanence) + RepanierSettings.bank_account, + _('with communication'), + customer.short_basket_name, + permanence) else: customer_payment_needed = "%s" % (_('Your account balance is sufficient')) else: @@ -122,7 +127,7 @@ def send(permanence_id): customer_last_balance = "" customer_payment_needed = "" long_basket_name = customer.long_basket_name if customer.long_basket_name is not None else customer.short_basket_name - order_customer_mail = repanier_settings['CONFIG'].order_customer_mail + order_customer_mail = RepanierSettings.config.order_customer_mail template = Template(order_customer_mail) context = djangoContext({ 'long_basket_name': long_basket_name, @@ -132,11 +137,12 @@ def send(permanence_id): 'customer_order_amount': number_format(order_amount, 2), 'customer_payment_needed': mark_safe(customer_payment_needed), 'customer_delivery_point': customer.delivery_point, - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe( + '%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) email = EmailMultiAlternatives( - "%s - %s - %s - %s" % (_('Order'), permanence, repanier_settings['GROUP_NAME'], long_basket_name), + "%s - %s - %s - %s" % (_('Order'), permanence, RepanierSettings.group_name, long_basket_name), strip_tags(html_content), sender_email, email_customer @@ -160,17 +166,17 @@ def send(permanence_id): if permanenceboard.customer: to_email_board.append(permanenceboard.customer.user.email) - order_staff_mail = repanier_settings['CONFIG'].order_staff_mail + order_staff_mail = RepanierSettings.config.order_staff_mail template = Template(order_staff_mail) context = djangoContext({ 'permanence': mark_safe('%s' % (settings.ALLOWED_HOSTS[0], reverse('order_view', args=(permanence.id,)), permanence)), 'board_composition': mark_safe(board_composition), 'board_composition_and_description': mark_safe(board_composition_and_description), - 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, repanier_settings['GROUP_NAME'])) + 'signature': mark_safe('%s
%s
%s' % (signature, sender_function, RepanierSettings.group_name)) }) html_content = template.render(context) email = EmailMultiAlternatives( - "%s - %s - %s" % (_('Permanence preparation list'), permanence, repanier_settings['GROUP_NAME']), + "%s - %s - %s" % (_('Permanence preparation list'), permanence, RepanierSettings.group_name), strip_tags(html_content), sender_email, to_email_board, @@ -181,7 +187,7 @@ def send(permanence_id): 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') email.attach_alternative(html_content, "text/html") - if not repanier_settings['SEND_ORDER_MAIL_TO_BOARD']: + if not RepanierSettings.send_order_mail_to_board: email.to = cc_email_staff email.cc = [] email.bcc = [] diff --git a/repanier/forms.py b/repanier/forms.py index 4847713c53b65e98196a28e57cc6dacf3b5a78dd..393dc1209d2fc858a430c75305dd2bc8fd3489a2 100644 --- a/repanier/forms.py +++ b/repanier/forms.py @@ -1,4 +1,3 @@ -from crispy_forms.bootstrap import AppendedText, StrictButton, FieldWithButtons from django import forms from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit @@ -8,9 +7,11 @@ from django.forms import Textarea from django.utils import translation from django.utils.translation import ugettext_lazy as _ from djangocms_text_ckeditor.widgets import TextEditorWidget -from apps import repanier_settings +from apps import RepanierSettings from const import * from models import LUT_DeliveryPoint, Staff +from picture.const import SIZE_M, SIZE_S +from picture.widgets import AjaxPictureWidget from widget import SelectWidgetBootstrap, SelectProducerOrderUnitWidget @@ -101,6 +102,11 @@ class CustomerForm(forms.Form): widget=SelectWidgetBootstrap, required=True ) + picture = forms.CharField( + label=_("picture"), + widget=AjaxPictureWidget(upload_to="customer", size=SIZE_S, bootstrap=True), + required=False) + memo = forms.CharField(label=_('About you'), widget=TextEditorWidget, required=False) # memo = forms.CharField(label=_('About you'), widget=Textarea, required=False) @@ -138,7 +144,7 @@ class CustomerForm(forms.Form): self.helper.label_class = 'col-lg-2' self.helper.field_class = 'col-lg-10' self.helper.add_input(Submit('submit', _('Update'))) - if not repanier_settings['DELIVERY_POINT']: + if not RepanierSettings.delivery_point: del self.fields['delivery_point'] @@ -172,6 +178,10 @@ class ProducerProductDescriptionForm(forms.Form): widget=SelectWidgetBootstrap, required=True ) + picture = forms.CharField( + label=_("picture"), + widget=AjaxPictureWidget(upload_to="product", size=SIZE_M, bootstrap=True), + required=False) # offer_description = forms.CharField(label=_('offer_description'), widget=Textarea, required=False) offer_description = forms.CharField(label=_('offer_description'), widget=TextEditorWidget, required=False) diff --git a/repanier/locale/fr/LC_MESSAGES/django.mo b/repanier/locale/fr/LC_MESSAGES/django.mo index f4d0a3048731bd543d430205a64611942a883d66..9783daa5a2c037254e4e17d37f9e058152fc0db6 100644 Binary files a/repanier/locale/fr/LC_MESSAGES/django.mo and b/repanier/locale/fr/LC_MESSAGES/django.mo differ diff --git a/repanier/locale/fr/LC_MESSAGES/django.po b/repanier/locale/fr/LC_MESSAGES/django.po index 707920f4a3b108bc6577e1862ec30d082210b57c..c8fefcc71881fc12801e82341999c8a1efbc8230 100644 --- a/repanier/locale/fr/LC_MESSAGES/django.po +++ b/repanier/locale/fr/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Repanier\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-05-23 13:47+0200\n" -"PO-Revision-Date: 2015-05-26 22:07+0100\n" +"POT-Creation-Date: 2015-07-05 16:03+0200\n" +"PO-Revision-Date: 2015-07-05 16:26+0100\n" "Last-Translator: Patrick Colmant \n" "Language-Team: Patrick Colmant \n" "Language: fr\n" @@ -18,350 +18,337 @@ msgstr "" "X-Generator: Poedit 1.7.1\n" "X-Poedit-SourceCharset: UTF-8\n" -#: admin.py:122 +#: admin.py:118 #, python-format msgid "Export purchases of %s" msgstr "Exporter la liste de produits achetés en %s" -#: admin.py:134 +#: admin.py:130 msgid "Opening mails" msgstr "✉ mails d'ouverture des commandes" -#: admin.py:141 +#: admin.py:137 msgid "Ordering mails" msgstr "✉ mails de clôture des commandes" -#: admin.py:150 +#: admin.py:146 msgid "Invoicing mails" msgstr "✉ mails de facturation des commandes" -#: admin.py:159 +#: admin.py:155 msgid "Advanced options" msgstr "Paramètres avancés" -#: admin.py:191 task/admin.py:252 -msgid "Export products of selected producer(s) as XSLX file" -msgstr "Exporter les produits des producteurs sélectionnés" +#: admin.py:187 +msgid "Export products of selected producer(s) as XSLX file at procuder's pices" +msgstr "Exporter les produits des producteurs sélectionnés au tarif producteur" -#: admin.py:196 task/admin.py:256 +#: admin.py:192 +msgid "Export products of selected producer(s) as XSLX file at customer's prices" +msgstr "Exporter les produits des producteurs sélectionnés au tarif consommateur" + +#: admin.py:197 msgid "Import products of selected producer(s) from a XLSX file" msgstr "Importer, mettre à jour les produits des producteurs sélectionnés" -#: admin.py:217 +#: admin.py:218 msgid "The prices have been recalculated." msgstr "Les prix ont été recalculés." -#: admin.py:219 +#: admin.py:220 msgid "recalculate prices" msgstr "Recalculer les prix" -#: admin.py:289 task/admin.py:358 templates/repanier/login.html:23 +#: admin.py:288 templates/repanier/login.html:23 #: templates/repanier/login.html.py:31 msgid "Username" msgstr "Login" -#: admin.py:291 task/admin.py:360 -msgid "" -"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" +#: admin.py:290 +msgid "Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" msgstr "Requis. 30 caractères ou moins. Lettres, chiffres et @/./+/-/_" -#: admin.py:294 task/admin.py:363 +#: admin.py:293 msgid "Enter a valid username." msgstr "Entrez un login valide" -#: admin.py:297 task/admin.py:367 templates/repanier/who_is_who.html:14 -#: templates/repanier/who_is_who.html:25 +#: admin.py:296 templates/repanier/who_is_who.html:20 +#: templates/repanier/who_is_who.html:39 msgid "Email" msgstr "E-mail" -#: admin.py:319 forms.py:121 +#: admin.py:315 forms.py:127 msgid "The phone number must ends with 4 digits, eventually separated" msgstr "Le n° ✆ principal doit comporter au moins 4 chiffres" -#: admin.py:339 task/admin.py:402 +#: admin.py:334 msgid "The given username must be set" msgstr "Le login doit être renseigné" -#: admin.py:340 task/admin.py:403 +#: admin.py:335 msgid "The given username is used by another user" msgstr "L'e-mail est utilisé par un autre utilisateur" -#: admin.py:342 task/admin.py:405 +#: admin.py:337 msgid "The given short_basket_name must be set" msgstr "Un nom du panier doit être renseigné" -#: admin.py:343 task/admin.py:406 +#: admin.py:338 msgid "The given short_basket_name is used by another user" msgstr "Ce nom de panier est déjà attribué à un autre utilisateur" -#: admin.py:351 task/admin.py:415 +#: admin.py:346 msgid "The given email must be set" msgstr "L'e-mail doit être renseigné" -#: admin.py:362 +#: admin.py:357 #, python-format msgid "The given email must end with %(allowed_extension)s" msgstr "L'e-mail doit se terminer par %(allowed_extension)s" -#: admin.py:370 forms.py:129 task/admin.py:428 +#: admin.py:365 forms.py:135 msgid "The given email is used by another user" msgstr "L'e-mail est utilisé par un autre utilisateur" -#: admin.py:426 models.py:380 task/admin.py:497 +#: admin.py:421 models.py:378 models_old.py:380 msgid "email" msgstr "E-mail" -#: admin.py:435 +#: admin.py:430 msgid "date joined" msgstr "Date d'inscription" -#: admin.py:443 +#: admin.py:438 msgid "last login" msgstr "Dernière connexion" -#: admin.py:631 admin.py:1270 task/admin.py:1115 task/task_invoice.py:558 +#: admin.py:639 admin.py:1249 task/task_invoice.py:558 msgid "Action performed." msgstr "Action effectuée." -#: admin.py:636 +#: admin.py:644 #, python-format msgid "Apply to %s" msgstr "Mettre à jour dans la %s" -#: admin.py:664 task/admin.py:642 +#: admin.py:672 msgid "flip_flop_select_for_offer_status for offer" msgstr "✔ en commande ↔ ✘ pas en commande" -#: admin.py:670 task/admin.py:677 +#: admin.py:678 msgid "duplicate product" msgstr "Créer un duplicata" -#: admin.py:769 admin.py:2047 +#: admin.py:783 admin.py:1922 admin.py:2045 msgid "Please select first a producer in the filter of previous screen" -msgstr "" -"Vous devez d'abord sélectionner un producteur via les filtres dans l'écran " -"précédent..." +msgstr "Vous devez d'abord sélectionner un producteur via les filtres dans l'écran précédent..." -#: admin.py:923 +#: admin.py:902 msgid "Export planned xlsx" msgstr "1 - Vérifier l'écran de commande avant l'ouverture" -#: admin.py:928 +#: admin.py:907 msgid "Export xlsx customers orders" msgstr "  - (Exporter les commandes consommateurs)" -#: admin.py:933 +#: admin.py:912 msgid "Import stock from a xlsx file" msgstr "  - (Importer, mettre à jour le stock)" -#: admin.py:938 +#: admin.py:917 msgid "Export xlsx producers orders" msgstr "  - (Exporter les commandes producteurs)" -#: admin.py:941 admin.py:961 admin.py:981 admin.py:1001 admin.py:1021 -#: admin.py:1041 admin.py:1209 admin.py:1290 admin.py:1310 task/admin.py:260 -#: task/admin.py:860 task/admin.py:904 task/admin.py:950 task/admin.py:979 -#: task/admin.py:1098 task/admin.py:1140 task/admin.py:1168 xslx/views.py:51 +#: admin.py:920 admin.py:940 admin.py:960 admin.py:980 admin.py:1000 +#: admin.py:1020 admin.py:1188 admin.py:1269 admin.py:1289 xslx/views.py:51 msgid "Action canceled by the user." msgstr "Action annulée par l'utilisateur." -#: admin.py:949 task/admin.py:892 +#: admin.py:928 msgid "Please, confirm the action : open and send offers" msgstr "Veuillez confirmer votre demande d'ouverture des commandes." -#: admin.py:958 task/admin.py:901 +#: admin.py:937 msgid "open and send offers" msgstr "2 - Ouvrir les commandes et prévenir les consommateurs par mail" -#: admin.py:969 +#: admin.py:948 msgid "Please, confirm the action : close orders" msgstr "Veuillez confirmer votre demande de clôture des commandes." -#: admin.py:978 +#: admin.py:957 msgid "close orders" msgstr "3 - Clôturer les commandes" -#: admin.py:989 +#: admin.py:968 msgid "Please, confirm the action : send orders" -msgstr "" -"Veuillez confirmer votre demande d'envoi des commandes par mail aux " -"producteurs et aux consommateurs." +msgstr "Veuillez confirmer votre demande d'envoi des commandes par mail aux producteurs et aux consommateurs." -#: admin.py:998 +#: admin.py:977 msgid "send orders1" -msgstr "" -"4 - Envoyer les commandes par mail aux consommateurs et aux producteurs" +msgstr "4 - Envoyer les commandes par mail aux consommateurs et aux producteurs" -#: admin.py:1009 +#: admin.py:988 msgid "Please, confirm the action : back to planned" msgstr "Veuillez confirmer votre demande de re-planification de la permanence." -#: admin.py:1018 +#: admin.py:997 msgid "back to planned" msgstr "  - (Retour au statut \"Planifié\")" -#: admin.py:1029 +#: admin.py:1008 msgid "Please, confirm the action : undo back to planned" -msgstr "" -"Veuillez confirmer votre demande d'annulation de re-planification de la " -"permanence." +msgstr "Veuillez confirmer votre demande d'annulation de re-planification de la permanence." -#: admin.py:1038 +#: admin.py:1017 msgid "undo back to planned" msgstr "  - (Annuler le retour au statut \"Planifié\")" -#: admin.py:1050 task/admin.py:995 -msgid "" -"Please, confirm the action : delete purchases. Be carefull : !!! THERE IS NO " -"WAY TO RESTORE THEM AUTOMATICALY !!!!" -msgstr "" -"Veuillez confirmer votre demande de suppression des commandes. " -"Prudence : !!! AUCUN RETOUR AUTOMATIQUE EN ARRIERE N'EST POSSIBLE !!! " +#: admin.py:1029 +msgid "Please, confirm the action : delete purchases. Be carefull : !!! THERE IS NO WAY TO RESTORE THEM AUTOMATICALY !!!!" +msgstr "Veuillez confirmer votre demande de suppression des commandes. Prudence : !!! AUCUN RETOUR AUTOMATIQUE EN ARRIERE N'EST POSSIBLE !!! " -#: admin.py:1059 +#: admin.py:1038 msgid "delete purchases1" msgstr "⚠ - Supprimer les commandes de la permanence" -#: admin.py:1091 +#: admin.py:1070 msgid "Duplicate this permanence next week" msgstr "  - (Générer 1 permanence hebdomadaire à partir de celle-ci)" -#: admin.py:1124 +#: admin.py:1103 msgid "Duplicate this permanence during 12 week" msgstr "  - (Générer 12 permanences hebdomadaires à partir de celle-ci)" -#: admin.py:1196 task/admin.py:1076 +#: admin.py:1175 msgid "Export orders prepared as XSLX file" msgstr "1 - Exporter la liste de préparation à facturer" -#: admin.py:1201 task/admin.py:1080 +#: admin.py:1180 msgid "Import orders prepared from a XLSX file" msgstr "2 - Importer, mettre à jour la liste de préparation à facturer" -#: admin.py:1206 task/admin.py:1095 +#: admin.py:1185 msgid "Preview invoices before sending them by email" msgstr "4 - Exporter le rapport comptable" -#: admin.py:1233 -msgid "" -"Please make the following payments, whose bank movements have been generated" -msgstr "" -"Veuillez effectuer les paiements suinvants. Les mouvements de compte " -"correspondants ont été générés." +#: admin.py:1212 +msgid "Please make the following payments, whose bank movements have been generated" +msgstr "Veuillez effectuer les paiements suinvants. Les mouvements de compte correspondants ont été générés." -#: admin.py:1256 task/task_invoice.py:538 +#: admin.py:1235 task/task_invoice.py:538 #, python-format msgid "You must first invoice the %(permanence)s." msgstr "Vous devez d'abord facturer la %(permanence)s." -#: admin.py:1260 task/admin.py:1128 +#: admin.py:1239 msgid "Please, confirm the action : generate the invoices" msgstr "Veuillez confirmer votre demande de calcul des factures." -#: admin.py:1275 task/admin.py:1137 +#: admin.py:1254 msgid "generate invoices" msgstr "3 - Produire les factures" -#: admin.py:1287 +#: admin.py:1266 msgid "archive" msgstr "Archiver" -#: admin.py:1298 task/admin.py:1156 +#: admin.py:1277 msgid "Please, confirm the action : send the invoices" -msgstr "" -"Veuillez confirmer votre demande d'envoi aux consommateurs et aux " -"producteurs des factures par mail." +msgstr "Veuillez confirmer votre demande d'envoi aux consommateurs et aux producteurs des factures par mail." -#: admin.py:1307 task/admin.py:1165 +#: admin.py:1286 msgid "send invoices" msgstr "5 - Envoyer les factures par mail" -#: admin.py:1318 task/admin.py:1197 +#: admin.py:1297 msgid "Please, confirm the action : cancel the invoices" msgstr "Veuillez confirmer votre demande d'annulation des factures." -#: admin.py:1327 task/admin.py:1206 +#: admin.py:1306 msgid "cancel latest invoices" msgstr "X - Annuler la dernière facturation" -#: admin.py:1334 +#: admin.py:1313 msgid "cancel archiving" msgstr "Annuler l'archivage" -#: admin.py:1458 admin.py:1723 xslx/xslx_invoice.py:256 +#: admin.py:1439 admin.py:1707 xslx/xslx_invoice.py:256 #: xslx/xslx_purchase.py:50 xslx/xslx_purchase.py:494 msgid "purchase price" msgstr "Prix d'achat" -#: admin.py:1460 templates/repanier/pre_order_form.html:30 -#: templates/repanier/producer_product_description_form.html:127 +#: admin.py:1441 templates/repanier/pre_order_form.html:30 +#: templates/repanier/producer_product_description_form.html:117 #: xslx/xslx_invoice.py:272 msgid "selling price" msgstr "Prix de vente" -#: admin.py:1501 admin.py:1775 models.py:1418 models.py:1432 models.py:1536 -#: models.py:1715 +#: admin.py:1481 admin.py:1755 models.py:1427 models.py:1441 models.py:1550 +#: models.py:1729 models_old.py:1418 models_old.py:1432 models_old.py:1536 +#: models_old.py:1715 msgid "producer amount invoiced" msgstr "Facturé par le producteur" -#: admin.py:1503 admin.py:1777 +#: admin.py:1483 admin.py:1757 msgid "apply rule of three" msgstr "Appliquer la règle de 3" -#: admin.py:1522 -msgid "" -"The rule of 3 is not applicable when the total_purchase_with_tax is negative" -msgstr "" -"La règle de 3 n'est pas applicable lorsque le montant facturé par le " -"producteur est négatif" +#: admin.py:1502 +msgid "The rule of 3 is not applicable when the total_purchase_with_tax is negative" +msgstr "La règle de 3 n'est pas applicable lorsque le montant facturé par le producteur est négatif" -#: admin.py:1526 +#: admin.py:1506 msgid "The stock may not be negative" msgstr "Le stock ne peut pas être négatif" -#: admin.py:1529 +#: admin.py:1509 msgid "The rule of 3 is not applicable when there is a stock" msgstr "La règle de 3 n'est pas applicable quand il y a du stock" -#: admin.py:1929 models.py:901 models.py:1472 xslx/xslx_order.py:178 -#: xslx/xslx_purchase.py:45 +#: admin.py:1909 models.py:908 models.py:1481 models_old.py:901 +#: models_old.py:1472 xslx/xslx_order.py:178 xslx/xslx_purchase.py:45 msgid "product" msgstr "Produit" -#: admin.py:1983 models.py:789 models.py:1480 xslx/xslx_product.py:43 -#: xslx/xslx_product.py:91 xslx/xslx_product.py:212 xslx/xslx_product.py:213 +#: admin.py:1981 models.py:796 models.py:1494 models_old.py:789 +#: models_old.py:1480 xslx/xslx_product.py:42 xslx/xslx_product.py:86 +#: xslx/xslx_product.py:207 xslx/xslx_product.py:208 msgid "department_for_customer" msgstr "Rayon" -#: admin.py:2156 admin.py:2157 admin.py:2162 admin.py:2163 task/admin.py:1519 -#: task/admin.py:1520 task/admin.py:1525 task/admin.py:1526 +#: admin.py:2154 admin.py:2155 admin.py:2160 admin.py:2161 msgid "Either a customer or a producer must be given." msgstr "Soit un consommateur, soit un producteur doit être renseigné." -#: admin.py:2165 admin.py:2166 task/admin.py:1528 task/admin.py:1529 +#: admin.py:2163 admin.py:2164 msgid "Only one customer or one producer must be given." msgstr "Soit un consommateur, soit un producteur doit être renseigné." -#: admin_filter.py:19 models.py:510 models.py:1047 models.py:1639 -#: task/admin.py:81 xslx/xslx_order.py:121 +#: admin_filter.py:19 models.py:508 models.py:1054 models.py:1653 +#: models_old.py:510 models_old.py:1047 models_old.py:1639 +#: xslx/xslx_order.py:121 msgid "producers" msgstr "Producteurs" -#: admin_filter.py:50 models.py:332 task/admin.py:111 +#: admin_filter.py:50 models.py:333 models_old.py:332 msgid "departments for customer" msgstr "Rayons" -#: admin_filter.py:75 models.py:645 models.py:1254 models.py:1300 -#: models.py:1409 models.py:1449 models.py:1871 models.py:2272 -#: task/admin.py:136 xslx/xslx_order.py:179 xslx/xslx_purchase.py:46 -#: xslx/xslx_purchase.py:484 +#: admin_filter.py:75 models.py:646 models.py:1263 models.py:1309 +#: models.py:1418 models.py:1458 models.py:1885 models.py:2286 +#: models_old.py:645 models_old.py:1254 models_old.py:1300 models_old.py:1409 +#: models_old.py:1449 models_old.py:1871 models_old.py:2272 +#: xslx/xslx_order.py:179 xslx/xslx_purchase.py:46 xslx/xslx_purchase.py:484 msgid "customer" msgstr "Consommateur" -#: admin_filter.py:98 models.py:509 models.py:775 models.py:1355 -#: models.py:1412 models.py:1482 models.py:1869 models.py:2269 models.py:2339 -#: task/admin.py:158 templates/repanier/pre_order_form.html:24 -#: xslx/xslx_order.py:177 xslx/xslx_purchase.py:44 xslx/xslx_purchase.py:477 +#: admin_filter.py:98 models.py:507 models.py:777 models.py:1364 +#: models.py:1421 models.py:1496 models.py:1883 models.py:2283 models.py:2353 +#: models_old.py:509 models_old.py:775 models_old.py:1355 models_old.py:1412 +#: models_old.py:1482 models_old.py:1869 models_old.py:2269 models_old.py:2339 +#: templates/repanier/pre_order_form.html:24 xslx/xslx_order.py:177 +#: xslx/xslx_purchase.py:44 xslx/xslx_purchase.py:477 #: xslx/xslx_purchase.py:478 msgid "producer" msgstr "Producteur" @@ -370,7 +357,8 @@ msgstr "Producteur" msgid "permanence" msgstr "permanence" -#: admin_filter.py:139 models.py:902 models.py:1647 xslx/xslx_product.py:187 +#: admin_filter.py:139 models.py:909 models.py:1661 models_old.py:902 +#: models_old.py:1647 xslx/xslx_product.py:182 msgid "products" msgstr "Produits" @@ -378,11 +366,11 @@ msgstr "Produits" msgid "only invoiced" msgstr "Seulement facturés" -#: apps.py:81 +#: apps.py:111 msgid "Other qty" msgstr "Autre qté" -#: auth_backend.py:75 templates/repanier/login.html:3 +#: auth_backend.py:74 templates/repanier/login.html:3 msgid "Too many attempt." msgstr "Trop de tentatives." @@ -449,11 +437,11 @@ msgstr "Mouvements de compte" msgid "%(name)s archived list" msgstr "%(name)s à archiver" -#: const.py:46 const_old.py:34 +#: const.py:46 msgid "disabled" msgstr "Désactivé" -#: const.py:47 const_old.py:35 +#: const.py:47 msgid "planned" msgstr "Planifié" @@ -465,11 +453,11 @@ msgstr "En cours de pré-ouverture" msgid "orders pre-opened" msgstr "Commandes pré-ouvertes" -#: const.py:50 const_old.py:36 +#: const.py:50 msgid "wait for open" msgstr "En cours d'ouverture" -#: const.py:51 const_old.py:37 +#: const.py:51 msgid "orders opened" msgstr "Commandes ouvertes" @@ -481,23 +469,23 @@ msgstr "En cours de clôture" msgid "orders closed" msgstr "Commandes clôturées" -#: const.py:54 const_old.py:38 +#: const.py:54 msgid "wait for send" msgstr "En cours de transmission" -#: const.py:55 const_old.py:39 +#: const.py:55 msgid "orders send to producers" msgstr "Commandes transmises" -#: const.py:56 const_old.py:40 +#: const.py:56 msgid "wait for done" msgstr "En cours de facturation" -#: const.py:57 const_old.py:41 +#: const.py:57 msgid "invoices validation test failed" msgstr "Factures non valides" -#: const.py:58 const_old.py:42 +#: const.py:58 msgid "done" msgstr "Commandes facturées" @@ -505,19 +493,19 @@ msgstr "Commandes facturées" msgid "archived" msgstr "Commandes archivées" -#: const.py:68 const_old.py:51 +#: const.py:68 msgid "freezer" msgstr "Surgélateur" -#: const.py:69 const_old.py:52 +#: const.py:69 msgid "fridge" msgstr "Frigo" -#: const.py:70 const_old.py:53 +#: const.py:70 msgid "loose, out of the basket" msgstr "En dehors du panier" -#: const.py:71 const_old.py:54 +#: const.py:71 msgid "into the basket" msgstr "Panier" @@ -549,22 +537,16 @@ msgstr "Vendu à la pièce, facturé selon le poids réel." msgid "bought per l" msgstr "Vendu au volume (en ℓ)." -#: const.py:94 const.py:109 const_old.py:80 const_old.py:98 -msgid "" -"As a deposit, a bag : always add this product to preparation list when the " -"customer has purchased something." +#: const.py:94 const.py:109 +msgid "As a deposit, a bag : always add this product to preparation list when the customer has purchased something." msgstr "Consigne reprise à la permanence." -#: const.py:96 const.py:111 const_old.py:82 const_old.py:101 -msgid "" -"As a subscription, common expense : add the minimal order quantity of this " -"product to each customer of the group" +#: const.py:96 const.py:111 +msgid "As a subscription, common expense : add the minimal order quantity of this product to each customer of the group" msgstr "Cotisation." -#: const.py:98 const.py:113 const_old.py:84 const_old.py:104 -msgid "" -"As a transportation cost : add the minimal order quantity of this product to " -"the basket representing the group." +#: const.py:98 const.py:113 +msgid "As a transportation cost : add the minimal order quantity of this product to the basket representing the group." msgstr "Frais de transport." #: const.py:118 @@ -579,257 +561,228 @@ msgstr "Vendu au poids" msgid "Sold by piece, invoiced following the weight" msgstr "Vendu à la pièce, facturé selon le poids réel" -#: const.py:131 const.py:140 const_old.py:116 const_old.py:125 +#: const.py:131 const.py:140 msgid "none" msgstr "Aucune" -#: const.py:132 const.py:141 const_old.py:117 const_old.py:126 +#: const.py:132 const.py:141 msgid "compensation 2%" msgstr "2% de compensation" -#: const.py:133 const.py:142 const_old.py:118 const_old.py:127 +#: const.py:133 const.py:142 msgid "compensation 6%" msgstr "6% de compensation" -#: const.py:134 const.py:143 const_old.py:119 const_old.py:128 +#: const.py:134 const.py:143 msgid "vat 6%" msgstr "6% de TVA" -#: const.py:135 const.py:144 const_old.py:120 const_old.py:129 +#: const.py:135 const.py:144 msgid "vat 12%" msgstr "12% de TVA" -#: const.py:136 const.py:145 const_old.py:121 const_old.py:130 +#: const.py:136 const.py:145 msgid "vat 21%" msgstr "21% de TVA" -#: const.py:154 const_old.py:138 +#: const.py:154 msgid "This is not the latest total" msgstr "Ceci n'est pas le dernier solde" -#: const.py:155 const_old.py:139 +#: const.py:155 msgid "This is the next latest bank total" msgstr "Ceci est le prochain solde. Le système est en train de le calculer." -#: const.py:156 const_old.py:140 +#: const.py:156 msgid "This is the latest bank total" msgstr "Ceci est le dernier solde." -#: const.py:165 models.py:203 +#: const.py:165 models.py:205 models_old.py:203 msgid "Permanence" msgstr "Permanence" -#: const.py:166 models.py:207 settings.py:5 +#: const.py:166 models.py:209 models_old.py:207 msgid "Closure" msgstr "Clôture" -#: const.py:167 models.py:211 task/admin.py:290 task/admin.py:302 +#: const.py:167 models.py:213 models_old.py:211 msgid "Delivery" msgstr "Livraison" -#: const.py:168 email/email_order.py:27 email/email_order.py:80 -#: email/email_order.py:139 models.py:216 +#: const.py:168 email/email_order.py:28 email/email_order.py:81 +#: email/email_order.py:140 models.py:217 models_old.py:216 msgid "Order" msgstr "Commande" -#: const_old.py:70 const_old.py:88 -msgid "/piece (loose)" -msgstr "Acheté et facturé à la pièce. → (•_•) consigne." - -#: const_old.py:71 const_old.py:92 -msgid "/piece (named)" -msgstr "" -"Acheté et facturé à la pièce. Le producteur étiquette le produit par " -"panier. → (•_•) consigne." - -#: const_old.py:72 const_old.py:89 -msgid "/Kg (loose)" -msgstr "Acheté et facturé au kg." - -#: const_old.py:73 const_old.py:93 -msgid "/Kg (named)" -msgstr "" -"Acheté et facturé au kg. Le producteur étiquette le produit par panier." - -#: const_old.py:74 const_old.py:90 -msgid "/piece -> Kg (loose)" -msgstr "Acheté par pièce, facturé au kg. → (•_•) poids moyen." - -#: const_old.py:75 const_old.py:95 -msgid "/piece -> Kg (named)" -msgstr "" -"Acheté par pièce, facturé au kg. Le producteur étiquette le produit par " -"panier. → (•_•) poids moyen." - -#: const_old.py:76 const_old.py:94 -msgid "/L (named)" -msgstr "Acheté et facturé au ℓ. Le producteur étiquette le produit par panier." - -#: const_old.py:77 const_old.py:91 -msgid "/piece -> L (loose)" -msgstr "" -"Acheté par pièce, facturé au ℓ. → (•_•) contenance moyenne et consigne." - -#: email/email_invoice.py:28 email/email_invoice.py:45 +#: email/email_invoice.py:29 email/email_invoice.py:46 #: templates/repanier/customer_invoice_form.html:29 -#: templates/repanier/invoice_form.html:29 -#: templates/repanier/invoicep_form.html:32 #: templates/repanier/producer_invoice_form.html:32 xslx/xslx_invoice.py:72 #: xslx/xslx_invoice.py:120 xslx/xslx_invoice.py:218 msgid "Payment" msgstr "Paiement" -#: email/email_invoice.py:60 email/email_invoice.py:84 task/admin.py:1087 +#: email/email_invoice.py:61 email/email_invoice.py:85 #: templates/repanier/customer_invoice_form.html:197 -#: templates/repanier/invoice_form.html:197 -#: templates/repanier/invoicep_form.html:192 #: templates/repanier/producer_invoice_form.html:192 xslx/xslx_invoice.py:223 msgid "Invoice" msgstr "Facture" -#: email/email_offer.py:46 email/email_offer.py:48 +#: email/email_offer.py:47 email/email_offer.py:49 msgid "offer" msgstr "liste de produits en offre et leurs prix" -#: email/email_offer.py:53 +#: email/email_offer.py:54 msgid "Pre-opening of orders" msgstr "Pré-ouverture des commandes" -#: email/email_offer.py:92 +#: email/email_offer.py:93 msgid "Opening of orders" msgstr "Ouverture des commandes" -#: email/email_order.py:110 +#: email/email_order.py:111 msgid "The balance of your account as of" msgstr "Le solde de votre compte en date du" -#: email/email_order.py:111 +#: email/email_order.py:112 msgid "is" msgstr "est de" -#: email/email_order.py:114 +#: email/email_order.py:115 msgid "Please pay" msgstr "Veuillez verser" -#: email/email_order.py:115 +#: email/email_order.py:116 msgid "to the bank account number" msgstr "sur le compte bancaire du groupe, no" -#: email/email_order.py:116 +#: email/email_order.py:117 msgid "with communication" msgstr ", avec comme communication" -#: email/email_order.py:118 +#: email/email_order.py:119 msgid "Your account balance is sufficient" msgstr "Le solde de votre compte est suffisant" -#: email/email_order.py:173 +#: email/email_order.py:174 msgid "Permanence preparation list" msgstr "Liste de préparation" -#: forms.py:18 templates/repanier/login.html:49 +#: forms.py:19 templates/repanier/login.html:49 msgid "Validation code" msgstr "Code de validation" -#: forms.py:63 forms.py:71 +#: forms.py:64 forms.py:72 msgid "Your Email" msgstr "Mon ✉ mail" -#: forms.py:64 forms.py:72 +#: forms.py:65 forms.py:73 msgid "Subject" msgstr "Sujet" -#: forms.py:65 forms.py:73 +#: forms.py:66 forms.py:74 msgid "Message" msgstr "Message" -#: forms.py:66 forms.py:82 +#: forms.py:67 forms.py:83 msgid "Send e-mail" msgstr "Envoyer l'e-mail" -#: forms.py:70 +#: forms.py:71 msgid "Recipient(s)" msgstr "Destinataire(s)" -#: forms.py:87 +#: forms.py:88 msgid "Your name" msgstr "Mon nom (par exemple John Smith)" -#: forms.py:88 +#: forms.py:89 +msgid "My phones numbers are visible to all members" +msgstr "Mes n° ✆ sont visibles à l'ensemble des membres" + +#: forms.py:90 msgid "Your main phone" msgstr "Mon n° ✆ principal" -#: forms.py:89 +#: forms.py:91 msgid "Your secondary phone" msgstr "Mon n° ✆ secondaire" -#: forms.py:90 -msgid "My phones numbers are visible to all members" -msgstr "Mes n° ✆ sont visibles à l'ensemble des membres" +#: forms.py:92 +msgid "My emails are visible to all members" +msgstr "Mes ✉ mail sont visibles à l'ensemble des membres" -#: forms.py:91 +#: forms.py:93 msgid "Your main email, used for password recovery and login" msgstr "Mon ✉ mail principale pour me connecter au site" -#: forms.py:92 +#: forms.py:94 msgid "Your secondary email" msgstr "Mon ✉ mail secondaire" -#: forms.py:93 -msgid "My emails are visible to all members" -msgstr "Mes ✉ mail sont visibles à l'ensemble des membres" - -#: forms.py:94 +#: forms.py:95 msgid "Your city" msgstr "Ma localité" -#: forms.py:97 models.py:302 models.py:569 +#: forms.py:98 models.py:306 models.py:567 models_old.py:302 models_old.py:569 msgid "delivery point" msgstr "Mon point de livraison" -#: forms.py:104 +#: forms.py:106 forms.py:182 models.py:266 models.py:269 models.py:571 +#: models.py:784 models.py:787 models.py:1483 models.py:1486 models_old.py:264 +#: models_old.py:293 models_old.py:322 models_old.py:351 models_old.py:782 +#: models_old.py:1474 +msgid "picture" +msgstr "Photo" + +#: forms.py:110 msgid "About you" msgstr "A propos de moi" -#: forms.py:140 templates/repanier/producer_product_description_form.html:226 +#: forms.py:146 templates/repanier/producer_product_description_form.html:225 msgid "Update" msgstr "Mettre à jour" -#: forms.py:146 models.py:702 models.py:881 models.py:1010 models.py:1457 -#: models.py:1738 models.py:1981 models.py:2063 models.py:2087 models.py:2254 -#: xslx/xslx_product.py:47 xslx/xslx_product.py:93 xslx/xslx_product.py:239 -#: xslx/xslx_product.py:291 xslx/xslx_product.py:296 xslx/xslx_product.py:334 +#: forms.py:152 models.py:703 models.py:888 models.py:1017 models.py:1466 +#: models.py:1752 models.py:1995 models.py:2077 models.py:2101 models.py:2268 +#: models_old.py:702 models_old.py:881 models_old.py:1010 models_old.py:1457 +#: models_old.py:1738 models_old.py:1981 models_old.py:2063 models_old.py:2087 +#: models_old.py:2254 xslx/xslx_product.py:46 xslx/xslx_product.py:88 +#: xslx/xslx_product.py:231 xslx/xslx_product.py:286 xslx/xslx_product.py:291 +#: xslx/xslx_product.py:329 msgid "long_name" msgstr "Nom étendu" -#: forms.py:148 models.py:858 models.py:1491 task/admin.py:634 -#: xslx/xslx_product.py:48 xslx/xslx_product.py:94 xslx/xslx_product.py:234 -#: xslx/xslx_product.py:235 +#: forms.py:154 models.py:865 models.py:1505 models_old.py:858 +#: models_old.py:1491 xslx/xslx_product.py:47 xslx/xslx_product.py:89 +#: xslx/xslx_product.py:226 xslx/xslx_product.py:227 msgid "order unit" msgstr "Mode de passation" -#: forms.py:161 models.py:805 models.py:1506 models.py:1935 models.py:1943 -#: xslx/xslx_invoice.py:254 xslx/xslx_product.py:52 xslx/xslx_product.py:97 +#: forms.py:167 models.py:812 models.py:1520 models.py:1949 models.py:1957 +#: models_old.py:805 models_old.py:1506 models_old.py:1935 models_old.py:1943 +#: xslx/xslx_invoice.py:254 xslx/xslx_product.py:51 xslx/xslx_product.py:92 #: xslx/xslx_purchase.py:48 xslx/xslx_stock.py:35 msgid "producer unit price" msgstr "Tarif producteur à l'unité" -#: forms.py:164 +#: forms.py:170 msgid "deposit to add" msgstr "+ Consigne" -#: forms.py:167 templates/repanier/pre_order_form.html:36 widget.py:162 +#: forms.py:173 templates/repanier/pre_order_form.html:36 widget.py:162 #: widget.py:168 widget.py:174 widget.py:190 widget.py:196 widget.py:202 #: xslx/xslx_stock.py:287 msgid "Stock" msgstr "Stock" -#: forms.py:170 models.py:831 models.py:1532 -#: templates/repanier/pre_order_form.html:33 xslx/xslx_purchase.py:51 +#: forms.py:176 models.py:838 models.py:1546 models_old.py:831 +#: models_old.py:1532 templates/repanier/pre_order_form.html:33 +#: xslx/xslx_purchase.py:51 msgid "tax" msgstr "Taxe" -#: forms.py:176 models.py:1011 models.py:1025 +#: forms.py:186 models.py:1018 models.py:1032 models_old.py:1011 +#: models_old.py:1025 msgid "offer_description" msgstr "Message accompagnant l'offre" @@ -869,1164 +822,915 @@ msgstr "Qui est qui" msgid "Me" msgstr "Moi" -#: models.py:50 +#: models.py:52 models_old.py:50 msgid "group name" msgstr "dénomination du groupe" -#: models.py:51 +#: models.py:53 models_old.py:51 msgid "test mode" msgstr "Activer le mode \"Test du site\"" -#: models.py:53 models.py:553 +#: models.py:55 models.py:551 models_old.py:53 models_old.py:553 msgid "login attempt counter" msgstr "compteur de tentatives de login" -#: models.py:59 +#: models.py:61 models_old.py:59 msgid "order name" msgstr "dénomination des commandes" -#: models.py:61 +#: models.py:63 models_old.py:61 msgid "display a pop up on the order form after this max week wo participation" msgstr "alerter le consommateur après ce nombre de semaines sans participation" -#: models.py:62 +#: models.py:64 models_old.py:62 msgid "0 mean : never display a pop up." msgstr "0 signifie : ne jamais alerter." -#: models.py:64 +#: models.py:66 models_old.py:64 msgid "send opening mail to customers" -msgstr "" -"Envoyer un ✉ mail d'ouverture des commandes aux consommateurs autorisés à " -"commander" +msgstr "Envoyer un ✉ mail d'ouverture des commandes aux consommateurs autorisés à commander" -#: models.py:65 +#: models.py:67 models_old.py:65 msgid "send order mail to customers" msgstr "Envoyer un ✉ mail récapitulatif de leur commande aux consommateurs" -#: models.py:66 +#: models.py:68 models_old.py:66 msgid "send order mail to producers" -msgstr "" -"Envoyer un ✉ mail récapitulatif de la commande du groupe aux producteurs" +msgstr "Envoyer un ✉ mail récapitulatif de la commande du groupe aux producteurs" -#: models.py:67 +#: models.py:69 models_old.py:67 msgid "send order mail to board" -msgstr "" -"Envoyer un ✉ mail récapitulatif des listes de préparations aux permanenciers" +msgstr "Envoyer un ✉ mail récapitulatif des listes de préparations aux permanenciers" -#: models.py:68 +#: models.py:70 models_old.py:68 msgid "send invoice mail to customers" msgstr "Envoyer un ✉ mail récapitulatif de leur facture aux consommateurs" -#: models.py:69 +#: models.py:71 models_old.py:69 msgid "send invoice mail to producers" msgstr "Envoyer un ✉ mail récapitulatif de leur livraison aux producteurs" -#: models.py:70 +#: models.py:72 models_old.py:70 msgid "activate invoice" msgstr "activer le module de comptabilité" -#: models.py:71 +#: models.py:73 models_old.py:71 msgid "activate stock" msgstr "activer le module de gestion du stock" -#: models.py:72 +#: models.py:74 models_old.py:72 msgid "display anonymous order form" msgstr "autoriser le visiteur anonyme à voir l'écran de commande consommateur" -#: models.py:73 +#: models.py:75 models_old.py:73 msgid "display producer on order form" -msgstr "" -"afficher la liste des fournisseurs dans l'écran de commande consommateur" +msgstr "afficher la liste des fournisseurs dans l'écran de commande consommateur" -#: models.py:74 +#: models.py:76 models_old.py:74 msgid "bank account" msgstr "compte bancaire" -#: models.py:75 +#: models.py:77 models_old.py:75 msgid "producer order rounded" msgstr "activer l'arrondi des commandes producteurs" -#: models.py:76 +#: models.py:78 models_old.py:76 msgid "producer pre-opening" -msgstr "" -"Activer la pré-ouverture des commandes pour les producteurs et la leur " -"annoncer par ✉ mail" +msgstr "Activer la pré-ouverture des commandes pour les producteurs et la leur annoncer par ✉ mail" -#: models.py:77 +#: models.py:79 models_old.py:77 msgid "accept child group" msgstr "accepter les sites enfants" -#: models.py:78 +#: models.py:80 models_old.py:78 msgid "display delivery point" msgstr "Activer les points de livraisons" -#: models.py:79 +#: models.py:81 models_old.py:79 msgid "display vat" msgstr "afficher la TVA" -#: models.py:81 models.py:582 +#: models.py:83 models.py:583 models_old.py:81 models_old.py:582 msgid "vat_id" msgstr "No de TVA" -#: models.py:82 +#: models.py:84 models_old.py:82 msgid "page break on customer check" -msgstr "" -"Insérer un saut de page entre deux consommateurs dans la liste de contrôle " -"des paniers" +msgstr "Insérer un saut de page entre deux consommateurs dans la liste de contrôle des paniers" -#: models.py:84 +#: models.py:86 models_old.py:84 msgid "offer customer mail" msgstr "Contenu du ✉" -#: models.py:86 +#: models.py:88 models_old.py:86 msgid "This message is send by mail to all customers when opening the order" msgstr "Ce mail ne comporte pas de pièce jointe." -#: models.py:97 +#: models.py:99 models_old.py:97 msgid "offer producer mail" msgstr "Contenu du ✉" -#: models.py:99 -msgid "" -"This message is send by mail to all producers when pre-opening the order" +#: models.py:101 models_old.py:99 +msgid "This message is send by mail to all producers when pre-opening the order" msgstr "Ce mail ne comporte pas de pièce jointe." -#: models.py:114 +#: models.py:116 models_old.py:114 msgid "order customer mail" msgstr "Contenu du ✉" -#: models.py:116 +#: models.py:118 models_old.py:116 msgid "This message is send by mail to each customer who have an order" msgstr "La commande du consommateur est jointe à ce mail." -#: models.py:129 +#: models.py:131 models_old.py:129 msgid "order staff mail" msgstr "Contenu du ✉" -#: models.py:131 +#: models.py:133 models_old.py:131 msgid "This message is send by mail to the preparation team and the staff" -msgstr "" -"Le récapitulatif des livraisons attendues et des listes de préparations est " -"joint à ce mail." +msgstr "Le récapitulatif des livraisons attendues et des listes de préparations est joint à ce mail." -#: models.py:145 +#: models.py:147 models_old.py:145 msgid "order producer mail" msgstr "Contenu du ✉" -#: models.py:147 -msgid "" -"This message is send by mail to each producer with the order of the group" +#: models.py:149 models_old.py:147 +msgid "This message is send by mail to each producer with the order of the group" msgstr "Le récapitulatif de la commande du groupe est jointe à ce mail." -#: models.py:158 +#: models.py:160 models_old.py:158 msgid "invoice customer mail" msgstr "Contenu du ✉" -#: models.py:160 +#: models.py:162 models_old.py:160 msgid "This message is send by mail to each customer with they invoice" msgstr "La facture de ce consommateur est en pièce jointe." -#: models.py:171 +#: models.py:173 models_old.py:171 msgid "invoice producer mail" msgstr "Contenu du ✉" -#: models.py:173 +#: models.py:175 models_old.py:173 msgid "This message is send by mail to each producer with they invoice" msgstr "Le récapitulatif de sa livraison est en pièce jointe." -#: models.py:186 +#: models.py:188 models_old.py:186 msgid "configuration" msgstr "configuration" -#: models.py:187 +#: models.py:189 models_old.py:187 msgid "configurations" msgstr "configurations" -#: models.py:204 +#: models.py:206 models_old.py:204 msgid "Permanences" msgstr "Permanences" -#: models.py:205 +#: models.py:207 models_old.py:205 msgid "Permanence on " msgstr "Permanence du " -#: models.py:208 settings.py:7 +#: models.py:210 models_old.py:208 msgid "Closures" msgstr "Clôtures" -#: models.py:209 settings.py:9 +#: models.py:211 models_old.py:209 msgid "Closure on " msgstr "Clôture du " -#: models.py:212 +#: models.py:214 models_old.py:212 msgid "Deliveries" msgstr "Livraisons" -#: models.py:213 +#: models.py:215 models_old.py:213 msgid "Delivery on " msgstr "Livraison du " -#: models.py:217 xslx/export_tools.py:36 +#: models.py:218 models_old.py:217 xslx/export_tools.py:36 msgid "Orders" msgstr "Commandes" -#: models.py:218 +#: models.py:219 models_old.py:218 msgid "Order on " msgstr "Commande du " -#: models.py:260 models.py:289 models.py:318 models.py:347 models.py:1024 +#: models.py:262 models.py:296 models.py:322 models.py:348 models.py:1031 +#: models_old.py:260 models_old.py:289 models_old.py:318 models_old.py:347 +#: models_old.py:1024 msgid "short_name" msgstr "Nom abrégé" -#: models.py:261 models.py:290 models.py:319 models.py:348 +#: models.py:263 models.py:297 models.py:323 models.py:349 models_old.py:261 +#: models_old.py:290 models_old.py:319 models_old.py:348 msgid "description" msgstr "Description" -#: models.py:264 models.py:293 models.py:322 models.py:351 models.py:782 -#: models.py:1474 -msgid "picture" -msgstr "Photo" - -#: models.py:266 models.py:295 models.py:324 models.py:353 models.py:428 -#: models.py:604 models.py:718 models.py:862 models.py:1550 +#: models.py:273 models.py:299 models.py:325 models.py:352 models.py:426 +#: models.py:605 models.py:719 models.py:720 models.py:869 models.py:1564 +#: models_old.py:266 models_old.py:295 models_old.py:324 models_old.py:353 +#: models_old.py:428 models_old.py:604 models_old.py:718 models_old.py:862 +#: models_old.py:1550 msgid "is_active" msgstr "Actif" -#: models.py:273 models.py:780 +#: models.py:280 models.py:782 models_old.py:273 models_old.py:780 msgid "production mode" msgstr "Label" -#: models.py:274 +#: models.py:281 models_old.py:274 msgid "production modes" msgstr "Labels" -#: models.py:303 +#: models.py:307 models_old.py:303 msgid "deliveries points" msgstr "Points de livraisons" -#: models.py:331 +#: models.py:332 models_old.py:331 msgid "department for customer" msgstr "Rayon" -#: models.py:361 +#: models.py:359 models_old.py:361 msgid "permanence role" msgstr "Rôle lors de la permanence" -#: models.py:362 +#: models.py:360 models_old.py:362 msgid "permanences roles" msgstr "Rôles lors des permanences" -#: models.py:375 +#: models.py:373 models_old.py:375 msgid "short_profile_name" msgstr "Nom abrégé" -#: models.py:378 +#: models.py:376 models_old.py:378 msgid "long_profile_name" msgstr "Nom étendu" -#: models.py:385 models.py:567 +#: models.py:383 models.py:565 models_old.py:385 models_old.py:567 msgid "language" msgstr "Langue" -#: models.py:387 models.py:574 models.py:731 +#: models.py:385 models.py:575 models.py:733 models_old.py:387 +#: models_old.py:574 models_old.py:731 msgid "phone1" msgstr "✆" -#: models.py:392 models.py:580 +#: models.py:390 models.py:581 models_old.py:392 models_old.py:580 msgid "phone2" msgstr "✆" -#: models.py:394 +#: models.py:392 models_old.py:394 msgid "fax" msgstr "Fax" -#: models.py:395 models.py:584 +#: models.py:393 models.py:585 models_old.py:395 models_old.py:584 msgid "address" msgstr "Adresse" -#: models.py:397 models.py:588 +#: models.py:395 models.py:589 models_old.py:397 models_old.py:588 msgid "memo" msgstr "Mémo" -#: models.py:400 models.py:402 +#: models.py:398 models.py:400 models_old.py:400 models_old.py:402 msgid "uuid" msgstr "Uuid" -#: models.py:403 +#: models.py:401 models_old.py:403 msgid "invoice by basket" msgstr "Facturation par panier" -#: models.py:404 models.py:1552 +#: models.py:402 models.py:1566 models_old.py:404 models_old.py:1552 msgid "manage stock" msgstr "Gérer le stock" -#: models.py:405 models.py:1513 +#: models.py:403 models.py:1527 models_old.py:405 models_old.py:1513 msgid "producer price are wo vat" msgstr "Les tarifs de ce producteur sont hors TVA" -#: models.py:408 models.py:1555 +#: models.py:406 models.py:1569 models_old.py:408 models_old.py:1555 msgid "price_list_multiplier" msgstr "Coefficient appliqué au tarif producteur" -#: models.py:409 models.py:1556 +#: models.py:407 models.py:1570 models_old.py:409 models_old.py:1556 msgid "This multiplier is applied to each price automatically imported/pushed." -msgstr "" -"0,9 signifie -10% sur le prix tarif. 1,06 signifie +6% sur le prix tarif." +msgstr "0,9 signifie -10% sur le prix tarif. 1,06 signifie +6% sur le prix tarif." -#: models.py:411 models.py:1558 +#: models.py:409 models.py:1572 models_old.py:411 models_old.py:1558 msgid "the resale price is set by the producer" msgstr "Les prix de vente consommateur sont imposés par le producteur." -#: models.py:417 +#: models.py:415 models_old.py:417 msgid "Default tax" msgstr "Taxes (valeur par défaut)" -#: models.py:420 models.py:596 models.py:1335 models.py:1390 +#: models.py:418 models.py:597 models.py:1344 models.py:1399 models_old.py:420 +#: models_old.py:596 models_old.py:1335 models_old.py:1390 msgid "date_balance" msgstr "Date de calcul du solde" -#: models.py:422 models.py:478 models.py:598 models.py:632 models.py:1337 -#: models.py:1392 +#: models.py:420 models.py:476 models.py:599 models.py:633 models.py:1346 +#: models.py:1401 models_old.py:422 models_old.py:478 models_old.py:598 +#: models_old.py:632 models_old.py:1337 models_old.py:1392 msgid "balance" msgstr "Solde" -#: models.py:425 models.py:601 +#: models.py:423 models.py:602 models_old.py:425 models_old.py:601 msgid "initial balance" msgstr "solde initial" -#: models.py:427 models.py:603 +#: models.py:425 models.py:604 models_old.py:427 models_old.py:603 msgid "represent_this_buyinggroup" msgstr "Représente ce groupement d'achats" -#: models.py:441 +#: models.py:439 models_old.py:441 msgid "his_products" msgstr "Produits" -#: models.py:444 +#: models.py:442 models_old.py:444 msgid "link to his products" msgstr "Produits" -#: models.py:500 +#: models.py:498 models_old.py:500 msgid "last invoice" msgstr "dernière facture" -#: models.py:505 templates/repanier/pre_order_form.html:30 -#: templates/repanier/producer_product_description_form.html:127 +#: models.py:503 models_old.py:505 templates/repanier/pre_order_form.html:30 +#: templates/repanier/producer_product_description_form.html:117 msgid "wo tax" msgstr "HTVA" -#: models.py:557 +#: models.py:555 models_old.py:557 msgid "short_basket_name" msgstr "Dénomination abrégée du panier" -#: models.py:560 +#: models.py:558 models_old.py:560 msgid "long_basket_name" msgstr "Dénomination étendue du panier" -#: models.py:562 +#: models.py:560 models_old.py:562 msgid "secondary email" msgstr "Adresse email secondaire" -#: models.py:586 +#: models.py:587 models_old.py:586 msgid "city" msgstr "Votre localité (information uniquement visible des membres)" -#: models.py:590 +#: models.py:591 models_old.py:590 msgid "show my mail to other members" msgstr "montrer mon ✉ mail aux autres membres" -#: models.py:592 +#: models.py:593 models_old.py:592 msgid "show my phone to other members" msgstr "montrer mon n° ✆ aux autres membres" -#: models.py:594 models.py:717 +#: models.py:595 models.py:718 models_old.py:594 models_old.py:717 msgid "password_reset_on" msgstr "Mot de passe réinitialisé le" -#: models.py:605 +#: models.py:606 models_old.py:605 msgid "may_order" msgstr "Peut commander" -#: models.py:646 models.py:1450 models.py:2357 +#: models.py:647 models.py:1459 models.py:2371 models_old.py:646 +#: models_old.py:1450 models_old.py:2357 msgid "customers" msgstr "Consommateurs" -#: models.py:697 +#: models.py:698 models_old.py:697 msgid "login" msgstr "Login" -#: models.py:699 +#: models.py:700 models_old.py:699 msgid "customer_responsible" msgstr "Consommateur responsable" -#: models.py:704 +#: models.py:705 models_old.py:704 msgid "function_description" msgstr "Description" -#: models.py:706 +#: models.py:707 models_old.py:706 msgid "is_reply_to_order_email" -msgstr "" -"Responsable des commande; cet ✉ est utilisé pour envoyer les offres et les " -"commandes" +msgstr "Responsable des commande; cet ✉ est utilisé pour envoyer les offres et les commandes" -#: models.py:708 +#: models.py:709 models_old.py:708 msgid "is_reply_to_invoice_email" -msgstr "" -"Responsable de la comptabilité; cet ✉ est utilisé pour envoyer les factures" +msgstr "Responsable de la comptabilité; cet ✉ est utilisé pour envoyer les factures" -#: models.py:710 +#: models.py:711 models_old.py:710 msgid "is_webmaster" msgstr "Webmaster" -#: models.py:712 +#: models.py:713 models_old.py:712 msgid "is_coordinator" msgstr "Coordinateur/trice" -#: models.py:738 +#: models.py:740 models_old.py:738 msgid "staff member" msgstr "Responsable" -#: models.py:739 +#: models.py:741 models_old.py:739 msgid "staff members" msgstr "Responsables" -#: models.py:785 models.py:1477 templates/repanier/invoicep_form.html:87 -#: templates/repanier/offerp_form.html:23 +#: models.py:792 models.py:1491 models_old.py:785 models_old.py:1477 #: templates/repanier/producer_invoice_form.html:87 msgid "reference" msgstr "Référence" -#: models.py:796 models.py:1502 +#: models.py:803 models.py:1516 models_old.py:796 models_old.py:1502 msgid "product_placement" msgstr "Emplacement du produit pendant la préparation" -#: models.py:797 models.py:1503 +#: models.py:804 models.py:1517 models_old.py:797 models_old.py:1503 msgid "used for helping to determine the order of preparation of this product" msgstr "Utilisé pour aider à déterminer l'ordre de préparation des paniers" -#: models.py:800 models.py:1495 xslx/xslx_product.py:51 -#: xslx/xslx_product.py:96 xslx/xslx_product.py:223 xslx/xslx_product.py:224 +#: models.py:807 models.py:1509 models_old.py:800 models_old.py:1495 +#: xslx/xslx_product.py:50 xslx/xslx_product.py:91 xslx/xslx_product.py:215 +#: xslx/xslx_product.py:216 msgid "order_average_weight" msgstr "Poids/contenance moyen(ne)" -#: models.py:801 models.py:1496 +#: models.py:808 models.py:1510 models_old.py:801 models_old.py:1496 msgid "if useful, average order weight (eg : 0,1 Kg [i.e. 100 gr], 3 Kg)" msgstr "1,7 = 1 kg 700 grammes ou 1,7 ℓ. 0,25 = 250 grammes ou 250 cℓ." -#: models.py:809 models.py:1510 models.py:1915 xslx/xslx_invoice.py:270 +#: models.py:816 models.py:1524 models.py:1929 models_old.py:809 +#: models_old.py:1510 models_old.py:1915 xslx/xslx_invoice.py:270 #: xslx/xslx_stock.py:35 msgid "customer unit price" msgstr "Tarif consommateur à l'unité" -#: models.py:813 models.py:816 models.py:1515 models.py:1518 +#: models.py:820 models.py:823 models.py:1529 models.py:1532 models_old.py:813 +#: models_old.py:816 models_old.py:1515 models_old.py:1518 msgid "vat" msgstr "TVA" -#: models.py:819 models.py:1521 +#: models.py:826 models.py:1535 models_old.py:819 models_old.py:1521 msgid "compensation" msgstr "Compensation" -#: models.py:820 models.py:1522 +#: models.py:827 models.py:1536 models_old.py:820 models_old.py:1522 msgid "compensation to add to the customer unit price" msgstr "Compensation à ajouter au prix de vente" -#: models.py:823 models.py:1325 models.py:1380 models.py:1525 models.py:1924 -#: models.py:1951 xslx/xslx_invoice.py:244 xslx/xslx_offer.py:47 -#: xslx/xslx_offer.py:119 xslx/xslx_product.py:54 xslx/xslx_product.py:98 -#: xslx/xslx_product.py:222 xslx/xslx_purchase.py:49 +#: models.py:830 models.py:1334 models.py:1389 models.py:1539 models.py:1938 +#: models.py:1965 models_old.py:823 models_old.py:1325 models_old.py:1380 +#: models_old.py:1525 models_old.py:1924 models_old.py:1951 +#: xslx/xslx_invoice.py:244 xslx/xslx_offer.py:47 xslx/xslx_offer.py:119 +#: xslx/xslx_product.py:53 xslx/xslx_product.py:93 xslx/xslx_product.py:214 +#: xslx/xslx_purchase.py:49 msgid "deposit" msgstr "Consigne" -#: models.py:824 models.py:1326 models.py:1381 +#: models.py:831 models.py:1335 models.py:1390 models_old.py:824 +#: models_old.py:1326 models_old.py:1381 msgid "deposit to add to the original unit price" msgstr "En supplément" -#: models.py:833 models.py:1562 xslx/xslx_stock.py:45 +#: models.py:840 models.py:1576 models_old.py:833 models_old.py:1562 +#: xslx/xslx_stock.py:45 msgid "Current stock" msgstr "Stock actuel" -#: models.py:835 models.py:1551 +#: models.py:842 models.py:1565 models_old.py:835 models_old.py:1551 msgid "limit maximum order qty of the group to stock qty" msgstr "Limiter les achats du groupe au stock disponible" -#: models.py:838 models.py:1572 xslx/xslx_product.py:57 -#: xslx/xslx_product.py:100 xslx/xslx_product.py:226 +#: models.py:845 models.py:1586 models_old.py:838 models_old.py:1572 +#: xslx/xslx_product.py:56 xslx/xslx_product.py:95 xslx/xslx_product.py:218 msgid "customer_minimum_order_quantity" msgstr "Quantité minimale de commande" -#: models.py:839 models.py:1573 +#: models.py:846 models.py:1587 models_old.py:839 models_old.py:1573 msgid "minimum order qty (eg : 0,1 Kg [i.e. 100 gr], 1 piece, 3 Kg)" msgstr "0,125 = 125 grammes ou 125 cℓ, 1 = 1 kg ou 1 pièce ou 1 ℓ." -#: models.py:842 models.py:1576 xslx/xslx_product.py:60 -#: xslx/xslx_product.py:101 xslx/xslx_product.py:228 xslx/xslx_product.py:229 +#: models.py:849 models.py:1590 models_old.py:842 models_old.py:1576 +#: xslx/xslx_product.py:59 xslx/xslx_product.py:96 xslx/xslx_product.py:220 +#: xslx/xslx_product.py:221 msgid "customer_increment_order_quantity" msgstr "Ensuite quantité de" -#: models.py:843 models.py:1577 +#: models.py:850 models.py:1591 models_old.py:843 models_old.py:1577 msgid "increment order qty (eg : 0,05 Kg [i.e. 50max 1 piece, 3 Kg)" msgstr "0,05 = 50 grammes ou 50 cℓ, 1 = 1 Kg ou 1 pièce ou 1 ℓ." -#: models.py:846 models.py:1580 xslx/xslx_product.py:64 -#: xslx/xslx_product.py:102 xslx/xslx_product.py:230 xslx/xslx_product.py:231 +#: models.py:853 models.py:1594 models_old.py:846 models_old.py:1580 +#: xslx/xslx_product.py:63 xslx/xslx_product.py:97 xslx/xslx_product.py:222 +#: xslx/xslx_product.py:223 msgid "customer_alert_order_quantity" msgstr "Quantité d'alerte" -#: models.py:847 models.py:1581 -msgid "" -"maximum order qty before alerting the customer to check (eg : 1,5 Kg, 12 " -"pieces, 9 Kg)" +#: models.py:854 models.py:1595 models_old.py:847 models_old.py:1581 +msgid "maximum order qty before alerting the customer to check (eg : 1,5 Kg, 12 pieces, 9 Kg)" msgstr "0,5 = 500 grammes ou 500 cℓ, 3 = 3 Kg ou 3 pièces ou 3 ℓ." -#: models.py:852 xslx/xslx_product.py:45 xslx/xslx_product.py:92 -#: xslx/xslx_product.py:278 xslx/xslx_product.py:324 +#: models.py:859 models_old.py:852 xslx/xslx_product.py:44 +#: xslx/xslx_product.py:87 xslx/xslx_product.py:273 xslx/xslx_product.py:319 msgid "is_into_offer" msgstr "En commande" -#: models.py:860 models.py:1492 +#: models.py:867 models.py:1506 models_old.py:860 models_old.py:1492 msgid "Individually wrapped by the producer" msgstr "Emballé séparément pour chaque membre par le producteur" -#: models.py:864 models.py:2297 +#: models.py:871 models.py:2311 models_old.py:864 models_old.py:2297 msgid "is_created_on" msgstr "Enregistrement créé le" -#: models.py:866 models.py:1051 models.py:2299 +#: models.py:873 models.py:1058 models.py:2313 models_old.py:866 +#: models_old.py:1051 models_old.py:2299 msgid "is_updated_on" msgstr "Enregistrement mis à jour le" -#: models.py:1015 +#: models.py:1022 models_old.py:1015 msgid "Product translation" msgstr "Produits" -#: models.py:1027 -msgid "" -"This message is send by mail to all customers when opening the order or on " -"top " -msgstr "" -"A l'ouverture des commandes, ce message est envoyé à chaque consommateur du " -"groupe autorisé à passer commande. Il est aussi affiché en haut de l'écran " -"des commandes." +#: models.py:1034 models_old.py:1027 +msgid "This message is send by mail to all customers when opening the order or on top " +msgstr "A l'ouverture des commandes, ce message est envoyé à chaque consommateur du groupe autorisé à passer commande. Il est aussi affiché en haut de l'écran des commandes." -#: models.py:1030 +#: models.py:1037 models_old.py:1030 msgid "invoice_description" msgstr "Message accompagnant la facture" -#: models.py:1032 -msgid "" -"This message is send by mail to all customers having bought something when " -"closing the permanence." -msgstr "" -"Ce message est envoyé par mail avec le détail de sa facture à chaque " -"consommateur du groupe." +#: models.py:1039 models_old.py:1032 +msgid "This message is send by mail to all customers having bought something when closing the permanence." +msgstr "Ce message est envoyé par mail avec le détail de sa facture à chaque consommateur du groupe." -#: models.py:1041 +#: models.py:1048 models_old.py:1041 msgid "permanence_status" msgstr "Statut" -#: models.py:1042 models.py:1057 -msgid "" -"status of the permanence from planned, orders opened, orders closed, send, " -"done" +#: models.py:1049 models.py:1064 models_old.py:1042 models_old.py:1057 +msgid "status of the permanence from planned, orders opened, orders closed, send, done" msgstr "Statut (Planifié, Ouvert, Commandes transmises, Facturé)" -#: models.py:1043 models.py:1259 models.py:1865 +#: models.py:1050 models.py:1268 models.py:1879 models_old.py:1043 +#: models_old.py:1259 models_old.py:1865 msgid "permanence_date" msgstr "Date de la permanence" -#: models.py:1044 +#: models.py:1051 models_old.py:1044 msgid "payment_date" msgstr "Date du paiement" -#: models.py:1049 +#: models.py:1056 models_old.py:1049 msgid "automatically_closed" msgstr "Clôturer automatiquement les commandes de la permanence" -#: models.py:1056 +#: models.py:1063 models_old.py:1056 msgid "highest permanence_status" msgstr "Statut le plus élevé de la permanence" -#: models.py:1125 models.py:1171 models.py:1218 +#: models.py:1134 models.py:1180 models.py:1227 models_old.py:1125 +#: models_old.py:1171 models_old.py:1218 msgid "Show" msgstr "Montrer" -#: models.py:1125 models.py:1171 models.py:1218 +#: models.py:1134 models.py:1180 models.py:1227 models_old.py:1125 +#: models_old.py:1171 models_old.py:1218 msgid "Hide" msgstr "Masquer" -#: models.py:1133 +#: models.py:1142 models_old.py:1133 msgid "No offer" msgstr "Pas d'offre" -#: models.py:1136 +#: models.py:1145 models_old.py:1136 msgid "producers in this permanence" msgstr "Offres de" -#: models.py:1173 +#: models.py:1182 models_old.py:1173 msgid "No purchase" msgstr "Pas d'achat" -#: models.py:1176 +#: models.py:1185 models_old.py:1176 msgid "customers in this permanence" msgstr "Achats par" -#: models.py:1221 +#: models.py:1230 models_old.py:1221 msgid "Empty board" msgstr "Tableau vide" -#: models.py:1224 models.py:1265 +#: models.py:1233 models.py:1274 models_old.py:1224 models_old.py:1265 msgid "permanence board" msgstr "Tableau des permanences" -#: models.py:1261 +#: models.py:1270 models_old.py:1261 msgid "permanence_role" msgstr "Rôle lors de la permanence" -#: models.py:1266 +#: models.py:1275 models_old.py:1266 msgid "permanences board" msgstr "Tableaux des permanences" -#: models.py:1285 +#: models.py:1294 models_old.py:1285 msgid "permanence in preparation" msgstr "Permanence en phase de préparation" -#: models.py:1286 +#: models.py:1295 models_old.py:1286 msgid "permanences in preparation" msgstr "Permanences en phase de préparation" -#: models.py:1293 +#: models.py:1302 models_old.py:1293 msgid "permanence done" msgstr "Permanence à facturer" -#: models.py:1294 +#: models.py:1303 models_old.py:1294 msgid "permanences done" msgstr "Permanences à facturer" -#: models.py:1305 models.py:1360 +#: models.py:1314 models.py:1369 models_old.py:1305 models_old.py:1360 msgid "invoice sort order" msgstr "Ordre de tri des factures" -#: models.py:1308 models.py:1363 +#: models.py:1317 models.py:1372 models_old.py:1308 models_old.py:1363 msgid "date_previous_balance" msgstr "Date de calcul du solde précédemment facturé" -#: models.py:1310 models.py:1365 +#: models.py:1319 models.py:1374 models_old.py:1310 models_old.py:1365 msgid "previous_balance" msgstr "Solde précédent facturé" -#: models.py:1313 templates/repanier/customer_invoice_form.html:105 -#: templates/repanier/invoice_form.html:105 -#: templates/repanier/invoicep_form.html:99 +#: models.py:1322 models_old.py:1313 +#: templates/repanier/customer_invoice_form.html:105 #: templates/repanier/producer_invoice_form.html:99 xslx/xslx_stock.py:219 #: xslx/xslx_stock.py:251 msgid "Total price" msgstr "Prix total" -#: models.py:1314 +#: models.py:1323 models_old.py:1314 msgid "Total price vat or compensation if applicable included" msgstr "Prix à la pièce ou au kg TVA ou compensation inclue" -#: models.py:1317 models.py:1372 +#: models.py:1326 models.py:1381 models_old.py:1317 models_old.py:1372 msgid "Total vat" msgstr "Total avec TVA" -#: models.py:1318 +#: models.py:1327 models_old.py:1318 msgid "Vat part of the total price" msgstr "TVA" -#: models.py:1321 models.py:1376 +#: models.py:1330 models.py:1385 models_old.py:1321 models_old.py:1376 msgid "Total compensation" msgstr "Compensation" -#: models.py:1322 +#: models.py:1331 models_old.py:1322 msgid "Compensation part of the total price" msgstr "Compensation" -#: models.py:1329 models.py:1384 models.py:2285 models.py:2309 +#: models.py:1338 models.py:1393 models.py:2299 models.py:2323 +#: models_old.py:1329 models_old.py:1384 models_old.py:2285 models_old.py:2309 msgid "bank_amount_in" msgstr "Montant crédité" -#: models.py:1329 models.py:1384 models.py:2285 +#: models.py:1338 models.py:1393 models.py:2299 models_old.py:1329 +#: models_old.py:1384 models_old.py:2285 msgid "payment_on_the_account" msgstr "Virement entrant" -#: models.py:1332 models.py:1387 models.py:2288 models.py:2321 +#: models.py:1341 models.py:1396 models.py:2302 models.py:2335 +#: models_old.py:1332 models_old.py:1387 models_old.py:2288 models_old.py:2321 msgid "bank_amount_out" msgstr "Montant débité" -#: models.py:1332 models.py:1387 models.py:2288 +#: models.py:1341 models.py:1396 models.py:2302 models_old.py:1332 +#: models_old.py:1387 models_old.py:2288 msgid "payment_from_the_account" msgstr "Virement sortant" -#: models.py:1344 +#: models.py:1353 models_old.py:1344 msgid "customer invoice" msgstr "Facture d'un consommateur" -#: models.py:1345 +#: models.py:1354 models_old.py:1345 msgid "customers invoices" msgstr "Factures de consommateurs" -#: models.py:1368 +#: models.py:1377 models_old.py:1368 msgid "Total amount" msgstr "Total avec TVA" -#: models.py:1369 models.py:1537 +#: models.py:1378 models.py:1551 models_old.py:1369 models_old.py:1537 msgid "Total purchase amount vat or compensation if applicable included" msgstr "Montant facturé par le producteur au kg TVA ou compensation inclue" -#: models.py:1373 +#: models.py:1382 models_old.py:1373 msgid "Vat part of the total purchased" msgstr "TVA" -#: models.py:1377 +#: models.py:1386 models_old.py:1377 msgid "Compensation part of the total purchased" msgstr "Compensation" -#: models.py:1399 +#: models.py:1408 models_old.py:1399 msgid "producer invoice" msgstr "Facture d'un producteur" -#: models.py:1400 +#: models.py:1409 models_old.py:1400 msgid "producers invoices" msgstr "Facture de producteurs" -#: models.py:1419 models.py:1424 models.py:1542 +#: models.py:1428 models.py:1433 models.py:1556 models_old.py:1419 +#: models_old.py:1424 models_old.py:1542 msgid "Total selling amount vat or compensation if applicable included" -msgstr "" -"Montant facturé au consommateur à la pièce ou au kg TVA ou compensation " -"inclue" +msgstr "Montant facturé au consommateur à la pièce ou au kg TVA ou compensation inclue" -#: models.py:1423 models.py:1541 +#: models.py:1432 models.py:1555 models_old.py:1423 models_old.py:1541 msgid "customer amount invoiced" msgstr "Montant facturé au consommateur" -#: models.py:1429 models.py:1712 models.py:1940 models.py:1948 +#: models.py:1438 models.py:1726 models.py:1954 models.py:1962 +#: models_old.py:1429 models_old.py:1712 models_old.py:1940 models_old.py:1948 #, python-format msgid "%(price)s €" msgstr "%(price)s €" -#: models.py:1437 +#: models.py:1446 models_old.py:1437 msgid "customer x producer invoice" msgstr "Consommateur X Producteur - Facture" -#: models.py:1438 +#: models.py:1447 models_old.py:1438 msgid "customers x producers invoices" msgstr "Consommateur X Producteur - Factures" -#: models.py:1464 +#: models.py:1473 models_old.py:1464 msgid "order sort order for optimization of order view rendering" msgstr "Ordre de tri pour optimiser le redu du formulaire de commande" -#: models.py:1484 models.py:1878 models.py:2291 +#: models.py:1498 models.py:1892 models.py:2305 models_old.py:1484 +#: models_old.py:1878 models_old.py:2291 msgid "producer_invoice" msgstr "Facture d'un producteur" -#: models.py:1507 -msgid "" -"producer unit price (/piece or /kg or /l), including vat, without deposit" +#: models.py:1521 models_old.py:1507 +msgid "producer unit price (/piece or /kg or /l), including vat, without deposit" msgstr "(/pièce, /kg ou /ℓ)" -#: models.py:1511 -msgid "" -"(/piece or /kg or /l), , including vat, without compensation, without deposit" +#: models.py:1525 models_old.py:1511 +msgid "(/piece or /kg or /l), , including vat, without compensation, without deposit" msgstr "(/pièce, /kg ou /ℓ)" -#: models.py:1526 +#: models.py:1540 models_old.py:1526 msgid "deposit to add to the unit price" msgstr "En supplément" -#: models.py:1546 models.py:1693 models.py:1821 models.py:1848 models.py:1893 -#: models.py:2078 models.py:2121 xslx/xslx_purchase.py:47 -#: xslx/xslx_purchase.py:492 xslx/xslx_purchase.py:493 +#: models.py:1560 models.py:1707 models.py:1835 models.py:1862 models.py:1907 +#: models.py:2092 models.py:2135 models_old.py:1546 models_old.py:1693 +#: models_old.py:1821 models_old.py:1848 models_old.py:1893 models_old.py:2078 +#: models_old.py:2121 xslx/xslx_purchase.py:47 xslx/xslx_purchase.py:492 +#: xslx/xslx_purchase.py:493 msgid "quantity invoiced" msgstr "Quantité à facturer" -#: models.py:1547 +#: models.py:1561 models_old.py:1547 msgid "quantity invoiced to our customer" msgstr "Quantité à facturer au consommateur" -#: models.py:1565 xslx/xslx_stock.py:42 xslx/xslx_stock.py:312 +#: models.py:1579 models_old.py:1565 xslx/xslx_stock.py:42 +#: xslx/xslx_stock.py:312 msgid "Add 2 stock" msgstr "Complémentaire" -#: models.py:1568 xslx/xslx_stock.py:43 +#: models.py:1582 models_old.py:1568 xslx/xslx_stock.py:43 msgid "Final stock" msgstr "Stock restant" -#: models.py:1685 models.py:1813 models.py:1840 +#: models.py:1699 models.py:1827 models.py:1854 models_old.py:1685 +#: models_old.py:1813 models_old.py:1840 #, python-format msgid "stock %(stock)s" msgstr "stock %(stock)s" -#: models.py:1688 models.py:1816 models.py:1843 +#: models.py:1702 models.py:1830 models.py:1857 models_old.py:1688 +#: models_old.py:1816 models_old.py:1843 #, python-format msgid "%(qty)s" msgstr "%(qty)s" -#: models.py:1690 models.py:1818 models.py:1845 +#: models.py:1704 models.py:1832 models.py:1859 models_old.py:1690 +#: models_old.py:1818 models_old.py:1845 #, python-format msgid "%(qty)s + stock %(stock)s" msgstr "%(qty)s + stock %(stock)s" -#: models.py:1751 templates/repanier/pre_order_form.html:55 -#: templates/repanier/pre_order_form.html:68 -#: templates/repanier/pre_order_form.html:70 -#: templates/repanier/producer_product_description_form.html:231 -#: templates/repanier/producer_product_description_form.html:235 tools.py:199 +#: models.py:1765 models_old.py:1751 templates/repanier/pre_order_form.html:59 +#: templates/repanier/pre_order_form.html:72 +#: templates/repanier/pre_order_form.html:74 +#: templates/repanier/producer_product_description_form.html:230 +#: templates/repanier/producer_product_description_form.html:234 tools.py:199 #: tools.py:203 tools.py:204 tools.py:209 tools.py:210 tools.py:238 msgid "kg" msgstr "kg" -#: models.py:1751 tools.py:186 tools.py:187 tools.py:253 tools.py:254 -#: tools.py:287 tools.py:288 tools.py:309 tools.py:310 tools.py:312 -#: tools.py:313 +#: models.py:1765 models_old.py:1751 tools.py:186 tools.py:187 tools.py:253 +#: tools.py:254 tools.py:287 tools.py:288 tools.py:309 tools.py:310 +#: tools.py:312 tools.py:313 msgid "piece" msgstr "pièce" -#: models.py:1754 +#: models.py:1768 models_old.py:1754 msgid "qty_display" msgstr "qté affichée" -#: models.py:1795 models.py:1827 models.py:1857 +#: models.py:1809 models.py:1841 models.py:1871 models_old.py:1795 +#: models_old.py:1827 models_old.py:1857 msgid "offer's item" msgstr "Produit proposé" -#: models.py:1796 models.py:1828 models.py:1858 +#: models.py:1810 models.py:1842 models.py:1872 models_old.py:1796 +#: models_old.py:1828 models_old.py:1858 msgid "offer's items" msgstr "Produits proposés" -#: models.py:1867 +#: models.py:1881 models_old.py:1867 msgid "offer_item" msgstr "Produit proposé" -#: models.py:1875 +#: models.py:1889 models_old.py:1875 msgid "customer_producer_invoice" msgstr "Facture consommateur X producteur" -#: models.py:1881 models.py:2294 +#: models.py:1895 models.py:2308 models_old.py:1881 models_old.py:2294 msgid "customer_invoice" msgstr "Facture d'un consommateur" -#: models.py:1885 models.py:1972 models.py:2007 xslx/xslx_order.py:180 +#: models.py:1899 models.py:1986 models.py:2021 models_old.py:1885 +#: models_old.py:1972 models_old.py:2007 xslx/xslx_order.py:180 #: xslx/xslx_stock.py:38 msgid "quantity ordered" msgstr "Qté commandée" -#: models.py:1890 +#: models.py:1904 models_old.py:1890 msgid "quantity for preparation order_by" msgstr "Qté pour trier les listes de préparation" -#: models.py:1896 -msgid "" -"Set if the invoiced price is the price with compensation, otherwise it's the " -"price with vat" +#: models.py:1910 models_old.py:1896 +msgid "Set if the invoiced price is the price with compensation, otherwise it's the price with vat" msgstr "Compensation appliquée" -#: models.py:1898 +#: models.py:1912 models_old.py:1898 msgid "producer row price" msgstr "Total au tarif producteur" -#: models.py:1901 +#: models.py:1915 models_old.py:1901 msgid "customer row price" msgstr "Total tarif consommateur" -#: models.py:1905 xslx/xslx_invoice.py:296 xslx/xslx_purchase.py:53 -#: xslx/xslx_purchase.py:491 +#: models.py:1919 models_old.py:1905 xslx/xslx_invoice.py:296 +#: xslx/xslx_purchase.py:53 xslx/xslx_purchase.py:491 msgid "comment" msgstr "Commentaire" -#: models.py:1958 models.py:1987 models.py:2069 models.py:2093 models.py:2260 +#: models.py:1972 models.py:2001 models.py:2083 models.py:2107 models.py:2274 +#: models_old.py:1958 models_old.py:1987 models_old.py:2069 models_old.py:2093 +#: models_old.py:2260 msgid "purchase" msgstr "Achat" -#: models.py:1959 models.py:1988 models.py:2070 models.py:2094 models.py:2261 +#: models.py:1973 models.py:2002 models.py:2084 models.py:2108 models.py:2275 +#: models_old.py:1959 models_old.py:1988 models_old.py:2070 models_old.py:2094 +#: models_old.py:2261 msgid "purchases" msgstr "Achats" -#: models.py:2274 +#: models.py:2288 models_old.py:2274 msgid "operation_date" msgstr "Date de l'opération" -#: models.py:2277 +#: models.py:2291 models_old.py:2277 msgid "operation_comment" msgstr "Commentaire" -#: models.py:2282 +#: models.py:2296 models_old.py:2282 msgid "Bank balance status" msgstr "Statut du solde du compte" -#: models.py:2362 +#: models.py:2376 models_old.py:2362 msgid "bank account movement" msgstr "Mouvement de compte" -#: models.py:2363 +#: models.py:2377 models_old.py:2363 msgid "bank account movements" msgstr "Mouvements de compte" -#: models.py:2381 +#: models.py:2395 models_old.py:2381 msgid "Initial balance" msgstr "Solde initial" -#: task/admin.py:180 -msgid "permanences" -msgstr "Permanences" - -#: task/admin.py:270 -msgid "Action canceled by the system. The latest bank total is in the future." -msgstr "Action refusée par le système. Le solde du compte est dans le futur." - -#: task/admin.py:275 -msgid "Action canceled by the system. No latest bank total has been set." -msgstr "" -"L'action a été annulée par le système. Le solde du compte n'a pas été défini." - -#: task/admin.py:290 task/admin.py:302 -msgid "up to" -msgstr "jusqu'au" - -#: task/admin.py:290 task/admin.py:302 -msgid "(included)" -msgstr "(inclus)" - -#: task/admin.py:290 task/admin.py:302 -msgid "Thanks!" -msgstr "Merci !" - -#: task/admin.py:311 -msgid "No bank account movement generated because there was nothing to pay." -msgstr "" -"Aucun mouvement généré car les soldes des producteurs sélectionnés sont nuls." - -#: task/admin.py:314 -msgid "" -"No bank account movement generated because there is a conflit with the " -"latest bank total." -msgstr "" -"Aucun mouvement généré car il y a un conflit entre cette demande et le solde " -"du compte." - -#: task/admin.py:318 -msgid "The bank account movement has been generated." -msgstr "Le mouvement de compte a été généré." - -#: task/admin.py:321 task/admin.py:328 -msgid "" -"At least one bank account movement has not been generated because there was " -"nothing to pay or there is a conflit with the latest bank total." -msgstr "" -"Certains mouvements n'ont pas été généré car soit il n'y avait rien à payer, " -"soit cela aurait produit un conflit avec le solde du compte." - -#: task/admin.py:325 -msgid "The bank account movement have been generated." -msgstr "Les mouvements de compte ont été générés." - -#: task/admin.py:335 -msgid "" -"Please, confirm the action : generate bank account movements corresponding " -"to the balance of each producer" -msgstr "" -"Veuillez confirmer cette action : Générer les mouvements de compte " -"correspondants au solde des producteurs sélectionnés." - -#: task/admin.py:344 -msgid "Generate bank account movements corresponding to the balances" -msgstr "" -"Générer les mouvements de compte correspondants au solde des producteurs " -"sélectionnés" - -#: task/admin.py:368 -msgid "First_name" -msgstr "Prénom" - -#: task/admin.py:369 -msgid "Last_name" -msgstr "Nom de famille" - -#: task/admin.py:645 task/task_product.py:18 -msgid "The product is duplicated." -msgstr "Le produit est dupliqué." - -#: task/admin.py:651 task/task_product.py:21 task/task_product.py:25 -msgid " (COPY)" -msgstr " (COPIE)" - -#: task/admin.py:666 task/task_product.py:40 -msgid "The products are duplicated." -msgstr "Les produits sont dupliqués." - -#: task/admin.py:669 -msgid "" -"The product has not been duplicated because a product with the same long " -"name already exists." -msgstr "" -"Le produit n'a pas été dupliqué car cela provoquerait la création de deux " -"produits ayant le même nom étendu pour un même producteur." - -#: task/admin.py:672 -msgid "" -"At least one product has not been duplicated because a product with the same " -"long name already exists." -msgstr "" -"Au moins un produit n'a pas été dupliqué car cela provoquerait la création " -"de deux produits ayant le même nom étendu pour un même producteur." - -#: task/admin.py:792 -msgid "" -"A permanence with the same distribution date and the same short_name already " -"exist. You must either change te distribution_date or the name." -msgstr "" -"Une permanence avec la même date de distribution et la même dénomination " -"existe déjà. Vous devez ou bien changer la date, ou bien la dénomintation." - -#: task/admin.py:845 -msgid "Export planified XLSX" -msgstr "1 - Vérifier l'écran de commande avant l'ouverture" - -#: task/admin.py:852 -msgid "Check" -msgstr "Contrôle" - -#: task/admin.py:857 -msgid "Export orders XLSX" -msgstr "3 - Exporter les commandes passées" - -#: task/admin.py:864 task/task_order.py:189 -msgid "The status of this permanence prohibit you to open and send offers." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." - -#: task/admin.py:873 task/task_order.py:208 task/task_order.py:217 -msgid "The offers are being generated." -msgstr "La permamence est en cours d'ouverture." - -#: task/admin.py:883 task/admin.py:928 task/task_order.py:236 -#: task/task_order.py:390 task/task_order.py:423 -msgid "" -"The action has been canceled by the system and an email send to the site " -"administrator." -msgstr "" -"L'action a été annulée par le système et un mail a été envoyé à son " -"administrateur." - -#: task/admin.py:886 task/admin.py:931 task/task_order.py:239 -#: task/task_order.py:393 task/task_order.py:426 -#, python-format -msgid "Action refused by the system. Please, retry in %d minutes." -msgstr "" -"Action refusée par le système. Une opération est déjà en cours depuis peu. " -"Veuillez réessayer dans %d minutes." - -#: task/admin.py:907 task/task_order.py:366 task/task_order.py:399 -msgid "The status of this permanence prohibit you to close it." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." - -#: task/admin.py:918 task/task_order.py:377 -msgid "The orders are being closed." -msgstr "Les commandes sont clôturées." - -#: task/admin.py:937 -msgid "Please, confirm the action : close and send orders" -msgstr "Veuillez confirmer votre demande de clôture des commandes." - -#: task/admin.py:947 -msgid "close and send orders" -msgstr "" -"4 - Clôturer les commandes et les envoyer par mail aux consommateurs et aux " -"producteurs" - -#: task/admin.py:953 -msgid "The status of this permanence prohibit you to go back to planified." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." +#: picture/widgets.py:278 +msgid "Remove" +msgstr "Supprimer" -#: task/admin.py:961 -msgid "The permanence is back to planified." -msgstr "La permanence est de retour au statut \"Planifié\"." - -#: task/admin.py:967 -msgid "Please, confirm the action : back to planified" -msgstr "Veuillez confirmer votre demande de re-planification de la permanence." +#: picture/widgets.py:279 +msgid "File is not an image" +msgstr "Le fichier téléchargé n"est pas une image" -#: task/admin.py:976 -msgid "back to planified" -msgstr "--- Retour au statut \"Planifié\"" - -#: task/admin.py:982 task/task_purchase.py:11 -msgid "The status of this permanence prohibit you to delete the purchases." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." - -#: task/admin.py:989 task/task_purchase.py:20 -msgid "" -"The purchases of this permanence have been deleted. There is no way to " -"restore them automaticaly." -msgstr "" -"Les commandes de cette permanence on été définitivement supprimées. Aucun " -"retour en arrière automatique n'est possible." - -#: task/admin.py:1004 -msgid "delete purchases" -msgstr "⚠ - Supprimer les commandes de la permanence" - -#: task/admin.py:1015 -msgid "Generate 12 weekly permanences starting from this" -msgstr "  - (Générer 12 permanences hebdomadaires à partir de celle-ci)" - -#: task/admin.py:1093 -msgid "You can only preview invoices when the permanence status is 'done'." -msgstr "" -"Vous pouvez uniquement exporter le rapport comptable des permanences dont le " -"statut est 'commandes facturées'." - -#: task/admin.py:1119 task/task_invoice.py:563 -msgid "" -"The permanence status says there is an error. You must cancel the invoice " -"then correct, before retrying." -msgstr "" -"La permanence est en erreur. Vous devez d'abord annuler la facturation, " -"ensuite corriger et finalement réessayer." - -#: task/admin.py:1122 task/task_invoice.py:530 -msgid "You can only generate invoices when the permanence status is 'send'." -msgstr "" -"Vous pouvez uniquement générer les factures des permanences dont le statut " -"est 'transmises aux producteurs'." - -#: task/admin.py:1147 task/task_invoice.py:576 -msgid "" -"Emails containing the invoices will be send to the customers and the " -"producers." -msgstr "" -"Des mails reprenant le détail facturé en pièce jointe sont en train d'être " -"envoyés vers les consommateurs et les producteurs." - -#: task/admin.py:1150 task/task_invoice.py:570 -msgid "The status of this permanence prohibit you to send invoices." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." - -#: task/admin.py:1174 -msgid "The status of this permanence prohibit you to close invoices." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." - -#: task/admin.py:1181 task/task_invoice.py:599 task/task_invoice.py:605 -msgid "The selected invoice has been canceled." -msgstr "La facture a été annulée." - -#: task/admin.py:1187 task/task_invoice.py:602 -msgid "The selected invoice is not the latest invoice." -msgstr "" -"La permanence sélectionnée n'étant pas la dernière permanence facturée, " -"l'annulation de ses factures ne peut avoir lieu." +#: picture/widgets.py:280 +msgid "Processing this image take too much time" +msgstr "Le traitement de cette image prend trop de temps" #: task/task_invoice.py:290 msgid "Lost" @@ -2044,59 +1748,100 @@ msgstr "Compensation pour perte" msgid "Compensation for profit" msgstr "Compensation pour profit" +#: task/task_invoice.py:530 +msgid "You can only generate invoices when the permanence status is 'send'." +msgstr "Vous pouvez uniquement générer les factures des permanences dont le statut est 'transmises aux producteurs'." + +#: task/task_invoice.py:563 +msgid "The permanence status says there is an error. You must cancel the invoice then correct, before retrying." +msgstr "La permanence est en erreur. Vous devez d'abord annuler la facturation, ensuite corriger et finalement réessayer." + +#: task/task_invoice.py:570 +msgid "The status of this permanence prohibit you to send invoices." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#: task/task_invoice.py:576 +msgid "Emails containing the invoices will be send to the customers and the producers." +msgstr "Des mails reprenant le détail facturé en pièce jointe sont en train d'être envoyés vers les consommateurs et les producteurs." + #: task/task_invoice.py:579 msgid "This action is not activated for your group." msgstr "Cette action n'est pas activée pour votre groupe." #: task/task_invoice.py:585 msgid "The status of this permanence prohibit you to cancel invoices." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#: task/task_invoice.py:599 task/task_invoice.py:605 +msgid "The selected invoice has been canceled." +msgstr "La facture a été annulée." + +#: task/task_invoice.py:602 +msgid "The selected invoice is not the latest invoice." +msgstr "La permanence sélectionnée n'étant pas la dernière permanence facturée, l'annulation de ses factures ne peut avoir lieu." #: task/task_invoice.py:618 msgid "The selected invoice has been restored." msgstr "L'archivage a été annulé." -#: task/task_order.py:125 +#: task/task_order.py:124 msgid "The status of this permanence prohibit you to go back to planned." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." -#: task/task_order.py:143 +#: task/task_order.py:142 msgid "The permanence is back to planned." msgstr "La permanence est de retour au statut \"Planifié\"." -#: task/task_order.py:149 +#: task/task_order.py:148 msgid "The status of this permanence prohibit you to go back to send." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." -#: task/task_order.py:168 +#: task/task_order.py:167 msgid "The permanence is back to pre-opened." msgstr "La permanence est de retour au statut \"Pré-ouverte\"." -#: task/task_order.py:175 +#: task/task_order.py:174 msgid "The permanence is back to open." msgstr "La permanence est de retour au statut \"Ouverte\"." -#: task/task_order.py:178 +#: task/task_order.py:177 msgid "The permanence is back to close." msgstr "La permanence est de retour au statut \"Cloturé\"." -#: task/task_order.py:183 +#: task/task_order.py:182 msgid "The permanence is back to send." msgstr "La permanence est de retour au statut \"Transmis\"." -#: task/task_order.py:199 +#: task/task_order.py:188 +msgid "The status of this permanence prohibit you to open and send offers." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#: task/task_order.py:198 msgid "A maximum of one permanence may be pre opened." -msgstr "" -"Une autre permanence est déjà \"Pré-ouverte\". Au plus une seule permanence " -"peut être \"Pré-ouverte\"." +msgstr "Une autre permanence est déjà \"Pré-ouverte\". Au plus une seule permanence peut être \"Pré-ouverte\"." + +#: task/task_order.py:207 task/task_order.py:216 +msgid "The offers are being generated." +msgstr "La permamence est en cours d'ouverture." + +#: task/task_order.py:235 task/task_order.py:389 task/task_order.py:422 +msgid "The action has been canceled by the system and an email send to the site administrator." +msgstr "L'action a été annulée par le système et un mail a été envoyé à son administrateur." + +#: task/task_order.py:238 task/task_order.py:392 task/task_order.py:425 +#, python-format +msgid "Action refused by the system. Please, retry in %d minutes." +msgstr "Action refusée par le système. Une opération est déjà en cours depuis peu. Veuillez réessayer dans %d minutes." -#: task/task_order.py:410 +#: task/task_order.py:365 task/task_order.py:398 +msgid "The status of this permanence prohibit you to close it." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#: task/task_order.py:376 +msgid "The orders are being closed." +msgstr "Les commandes sont clôturées." + +#: task/task_order.py:409 msgid "The orders are being send." msgstr "Les commandes sont en train d'être envoyées." @@ -2107,17 +1852,33 @@ msgstr "Livraison %(current_site)s - %(permanence)s. Merci!" #: task/task_producer.py:46 #, python-format -msgid "" -"Deliveries %(current_site)s - up to the %(permanence)s (included). Thanks!" -msgstr "" -"Livraisons %(current_site)s - jusqu'à la %(permanence)s (inclue). Merci!" +msgid "Deliveries %(current_site)s - up to the %(permanence)s (included). Thanks!" +msgstr "Livraisons %(current_site)s - jusqu'à la %(permanence)s (inclue). Merci!" #: task/task_producer.py:52 #, python-format -msgid "" -"Deliveries %(current_site)s - up to %(payment_date)s (included). Thanks!" -msgstr "" -"Livraisons %(current_site)s - jusqu'au %(payment_date)s (inclu). Merci!" +msgid "Deliveries %(current_site)s - up to %(payment_date)s (included). Thanks!" +msgstr "Livraisons %(current_site)s - jusqu'au %(payment_date)s (inclu). Merci!" + +#: task/task_product.py:18 +msgid "The product is duplicated." +msgstr "Le produit est dupliqué." + +#: task/task_product.py:21 task/task_product.py:25 +msgid " (COPY)" +msgstr " (COPIE)" + +#: task/task_product.py:40 +msgid "The products are duplicated." +msgstr "Les produits sont dupliqués." + +#: task/task_purchase.py:11 +msgid "The status of this permanence prohibit you to delete the purchases." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#: task/task_purchase.py:20 +msgid "The purchases of this permanence have been deleted. There is no way to restore them automaticaly." +msgstr "Les commandes de cette permanence on été définitivement supprimées. Aucun retour en arrière automatique n'est possible." #: templates/repanier/cache_part_b.html:6 #: templates/repanier/cache_part_b.html:14 @@ -2145,46 +1906,37 @@ msgstr "/ pc" #: templates/repanier/cache_part_b.html:16 #: templates/repanier/cache_part_c.html:16 -#: templates/repanier/order_form_std.html:167 -#: templates/repanier/order_form_std.html:180 #: templates/repanier/order_product_preview.html:34 tools.py:159 msgid "/ piece" msgstr "/ pièce" #: templates/repanier/cache_part_d.html:5 -#: templates/repanier/order_form_std.html:100 msgid " P " msgstr " P " #: templates/repanier/cache_part_d.html:9 #: templates/repanier/order_form.html:120 #: templates/repanier/order_form.html:132 -#: templates/repanier/order_form_std.html:71 -#: templates/repanier/order_form_std.html:104 msgid "All producers" msgstr "Tous les producteurs" #: templates/repanier/cache_part_d.html:19 -#: templates/repanier/order_form_std.html:112 msgid " D " msgstr " R " #: templates/repanier/cache_part_d.html:23 #: templates/repanier/order_form.html:147 #: templates/repanier/order_form.html:166 -#: templates/repanier/order_form_std.html:82 -#: templates/repanier/order_form_std.html:116 msgid "All departments" msgstr "Tous les rayons" -#: templates/repanier/cache_part_e.html:11 views.py:315 views.py:317 +#: templates/repanier/cache_part_e.html:10 views.py:320 views.py:322 msgid "There is no more product's information" msgstr "Il n'y a pas d'autre information sur ce produit." #: templates/repanier/communication.html:2 msgid "Your are registered to the following task :" -msgstr "" -"Vous êtes inscrit(e)s pour les tâches suivantes au tableau des permanences :" +msgstr "Vous êtes inscrit(e)s pour les tâches suivantes au tableau des permanences :" #: templates/repanier/communication.html:8 msgid "No task found." @@ -2213,119 +1965,85 @@ msgid "Done" msgstr "Terminé" #: templates/repanier/confirm_admin_invoice.html:6 -msgid "" -"when producing invoices, generate bank account movements corresponding to " -"the balance of the selected producers" +msgid "when producing invoices, generate bank account movements corresponding to the balance of the selected producers" msgstr "veuillez indiquer les producteurs que vous souhaitez payer" #: templates/repanier/confirm_admin_invoice.html:18 -msgid "" -"Yes, I'm sure and I have already encoded the customer's provisions into the " -"bank account" -msgstr "" -"Oui, je suis sûr ET J'AI déjà encodé les mouvements bancaires correspondants " -"aux provisions des membres" +msgid "Yes, I'm sure and I have already encoded the customer's provisions into the bank account" +msgstr "Oui, je suis sûr ET J'AI déjà encodé les mouvements bancaires correspondants aux provisions des membres" #: templates/repanier/customer_invoice_form.html:16 -#: templates/repanier/invoice_form.html:16 -#: templates/repanier/invoicep_form.html:16 #: templates/repanier/producer_invoice_form.html:16 msgid "New balance" msgstr "Nouveau solde" #: templates/repanier/customer_invoice_form.html:21 -#: templates/repanier/invoice_form.html:21 -#: templates/repanier/invoicep_form.html:21 #: templates/repanier/producer_invoice_form.html:21 msgid "Bank movement(s) :" msgstr "Mouvement(s) de compte :" #: templates/repanier/customer_invoice_form.html:26 -#: templates/repanier/invoice_form.html:26 -#: templates/repanier/invoicep_form.html:26 #: templates/repanier/producer_invoice_form.html:26 xslx/xslx_order.py:175 #: xslx/xslx_purchase.py:43 msgid "Date" msgstr "Date" #: templates/repanier/customer_invoice_form.html:32 -#: templates/repanier/invoice_form.html:32 -#: templates/repanier/invoicep_form.html:29 #: templates/repanier/producer_invoice_form.html:29 msgid "Refund" msgstr "Remboursement" #: templates/repanier/customer_invoice_form.html:35 #: templates/repanier/customer_invoice_form.html:108 -#: templates/repanier/invoice_form.html:35 -#: templates/repanier/invoice_form.html:108 -#: templates/repanier/invoicep_form.html:35 #: templates/repanier/producer_invoice_form.html:35 msgid "Comment" msgstr "Commentaire" #: templates/repanier/customer_invoice_form.html:61 -#: templates/repanier/invoice_form.html:61 -#: templates/repanier/invoicep_form.html:61 #: templates/repanier/producer_invoice_form.html:61 msgid "No bank movement found" msgstr "Pas de mouvement banquaire" #: templates/repanier/customer_invoice_form.html:64 -#: templates/repanier/invoice_form.html:64 -#: templates/repanier/invoicep_form.html:64 #: templates/repanier/producer_invoice_form.html:64 msgid "Purchase(s) :" msgstr "Achat(s) :" #: templates/repanier/customer_invoice_form.html:68 -#: templates/repanier/invoice_form.html:68 -#: templates/repanier/invoicep_form.html:67 #: templates/repanier/producer_invoice_form.html:67 msgid "This price include" msgstr "Ce montant inclu" #: templates/repanier/customer_invoice_form.html:69 -#: templates/repanier/invoice_form.html:69 -#: templates/repanier/invoicep_form.html:68 #: templates/repanier/producer_invoice_form.html:68 xslx/xslx_invoice.py:258 #: xslx/xslx_invoice.py:277 xslx/xslx_invoice.py:283 msgid "Vat" msgstr "(TVA)" #: templates/repanier/customer_invoice_form.html:74 -#: templates/repanier/invoice_form.html:74 -#: templates/repanier/invoicep_form.html:73 #: templates/repanier/producer_invoice_form.html:73 xslx/xslx_invoice.py:287 #: xslx/xslx_invoice.py:293 msgid "Compensation" msgstr "(Compensation)" #: templates/repanier/customer_invoice_form.html:80 -#: templates/repanier/invoice_form.html:80 -#: templates/repanier/invoicep_form.html:79 #: templates/repanier/producer_invoice_form.html:79 xslx/xslx_order.py:459 #: xslx/xslx_order.py:681 xslx/xslx_order.py:792 xslx/xslx_stock.py:36 msgid "Deposit" msgstr "Consigne" -#: templates/repanier/customer_invoice_form.html:90 -#: templates/repanier/invoice_form.html:90 xslx/xslx_invoice.py:235 +#: templates/repanier/customer_invoice_form.html:90 xslx/xslx_invoice.py:235 #: xslx/xslx_offer.py:41 xslx/xslx_offer.py:112 xslx/xslx_order.py:788 #: xslx/xslx_order.py:1022 msgid "Producer" msgstr "Producteur" -#: templates/repanier/customer_invoice_form.html:92 -#: templates/repanier/invoice_form.html:92 xslx/xslx_invoice.py:237 +#: templates/repanier/customer_invoice_form.html:92 xslx/xslx_invoice.py:237 #: xslx/xslx_offer.py:42 xslx/xslx_offer.py:114 msgid "Department" msgstr "Rayon" #: templates/repanier/customer_invoice_form.html:96 -#: templates/repanier/invoice_form.html:96 -#: templates/repanier/invoicep_form.html:90 -#: templates/repanier/offerp_form.html:26 #: templates/repanier/pre_order_form.html:27 #: templates/repanier/producer_invoice_form.html:90 xslx/xslx_invoice.py:240 #: xslx/xslx_offer.py:44 xslx/xslx_offer.py:116 xslx/xslx_order.py:457 @@ -2334,58 +2052,41 @@ msgid "Product" msgstr "Produit" #: templates/repanier/customer_invoice_form.html:99 -#: templates/repanier/invoice_form.html:99 -#: templates/repanier/invoicep_form.html:93 #: templates/repanier/producer_invoice_form.html:93 msgid "Qty" msgstr "Qté" #: templates/repanier/customer_invoice_form.html:102 -#: templates/repanier/invoice_form.html:102 -#: templates/repanier/invoicep_form.html:96 -#: templates/repanier/offerp_form.html:29 #: templates/repanier/producer_invoice_form.html:96 msgid "Unit price" msgstr "Prix unitaire" #: templates/repanier/customer_invoice_form.html:144 -#: templates/repanier/invoice_form.html:144 -#: templates/repanier/invoicep_form.html:136 #: templates/repanier/producer_invoice_form.html:136 msgid "No purchase found" msgstr "Pas d'achat trouvé" #: templates/repanier/customer_invoice_form.html:146 -#: templates/repanier/invoice_form.html:146 -#: templates/repanier/invoicep_form.html:138 #: templates/repanier/producer_invoice_form.html:138 msgid "Previous balance" msgstr "Solde précédent" #: templates/repanier/customer_invoice_form.html:160 -#: templates/repanier/invoice_form.html:160 -#: templates/repanier/invoicep_form.html:152 -#: templates/repanier/offerp_form.html:50 #: templates/repanier/order_form.html:381 #: templates/repanier/permanence_form.html:76 -#: templates/repanier/pre_order_form.html:112 -#: templates/repanier/producer_invoice_form.html:152 views.py:331 views.py:368 +#: templates/repanier/pre_order_form.html:116 +#: templates/repanier/producer_invoice_form.html:152 views.py:337 views.py:376 msgid "Anonymous" msgstr "Anonyme" #: templates/repanier/customer_invoice_form.html:166 -#: templates/repanier/invoice_form.html:166 -#: templates/repanier/invoicep_form.html:158 -#: templates/repanier/offerp_form.html:56 #: templates/repanier/permanence_form.html:82 -#: templates/repanier/pre_order_form.html:118 +#: templates/repanier/pre_order_form.html:122 #: templates/repanier/producer_invoice_form.html:158 msgid "Retry5" msgstr "Veuillez réessayer (5)" #: templates/repanier/customer_invoice_form.html:178 -#: templates/repanier/invoice_form.html:178 -#: templates/repanier/invoicep_form.html:170 #: templates/repanier/permanence_form.html:94 #: templates/repanier/producer_invoice_form.html:170 msgid "Retry7" @@ -2404,14 +2105,8 @@ msgid "Logout" msgstr "Déconnexion" #: templates/repanier/logged_out.html:10 -msgid "" -"You are now disconnected. You can go right now on the login page or on the home page." -msgstr "" -"Vous êtes maintenant déconnecté. Vous pouvez vous re connecter ou vous rendre à " -"la page d'accueil." +msgid "You are now disconnected. You can go right now on the login page or on the home page." +msgstr "Vous êtes maintenant déconnecté. Vous pouvez vous re connecter ou vous rendre à la page d'accueil." #: templates/repanier/login.html:8 templates/repanier/login.html.py:54 msgid "Login" @@ -2429,71 +2124,49 @@ msgstr "Validation (les 4 derniers chiffres de votre n° ✆)" msgid "Password lost" msgstr "Mot de passe perdu ?" -#: templates/repanier/me.html:5 templates/repanier/me_form.html:5 -#: templates/repanier/producer_product_description_form.html:10 +#: templates/repanier/me_form.html:6 +#: templates/repanier/producer_product_description_form.html:9 msgid "Update done." msgstr "Mise à jour effectuée." -#: templates/repanier/me.html:6 templates/repanier/me_form.html:6 -msgid "" -"All informations bellow are visible to all members except your phone and " -"email. You have the choice to specify if your phone and/or mail are visible " -"to others members." -msgstr "" -"Toutes les informations ci-dessous sont visibles de tous les membres excepté " -"vos ✆ et ✉ mail.
Vous avez le choix de spécifier si vos ✆ et ✉ mail sont " -"visibles des autres membres." - -#: templates/repanier/offerp_form.html:32 -msgid "Maximum quantity" -msgstr "Qté max" +#: templates/repanier/me_form.html:7 +msgid "All informations bellow are visible to all members except your phone and email. You have the choice to specify if your phone and/or mail are visible to others members." +msgstr "Toutes les informations ci-dessous sont visibles de tous les membres excepté vos ✆ et ✉ mail.
Vous avez le choix de spécifier si vos ✆ et ✉ mail sont visibles des autres membres." #: templates/repanier/order_form.html:65 msgid "You need to log in" -msgstr "" -"La possibilité de commander ou de voir votre historique n'est valide que si " -"vous êtes inscrit et connecté.
En tant que visiteur anonyme, vous pouvez " -"néanmoins parcourir la liste des produits en offre." +msgstr "La possibilité de commander ou de voir votre historique n'est valide que si vous êtes inscrit et connecté.
En tant que visiteur anonyme, vous pouvez néanmoins parcourir la liste des produits en offre." #: templates/repanier/order_form.html:67 msgid "Login me" msgstr "Me connecter" #: templates/repanier/order_form.html:75 -#: templates/repanier/order_form_std.html:318 #: templates/repanier/permanence_form.html:101 msgid "You are not allowed to order products" -msgstr "" -"La fonctionnalité de commande n'est pas activée sur votre compte. Si vous " -"souhaitez commander, veuillez vous adresser aux responsables de votre groupe." +msgstr "La fonctionnalité de commande n'est pas activée sur votre compte. Si vous souhaitez commander, veuillez vous adresser aux responsables de votre groupe." #: templates/repanier/order_form.html:99 msgid "Search" msgstr "Rechercher" #: templates/repanier/order_form.html:112 -#: templates/repanier/order_form_std.html:65 msgid "Purchased product" msgstr "Mon panier" #: templates/repanier/order_form.html:212 -#: templates/repanier/order_form_std.html:249 msgid "No offer found" msgstr "Pas de produit trouvé" #: templates/repanier/order_form.html:230 #: templates/repanier/order_form.html:250 #: templates/repanier/order_form.html:275 -#: templates/repanier/order_form_std.html:213 -#: templates/repanier/order_form_std.html:240 -#: templates/repanier/pre_order_form.html:95 -#: templates/repanier/producer_product_description_form.html:228 -#: templates/repanier/widget/preview_product_order.html:19 +#: templates/repanier/pre_order_form.html:99 +#: templates/repanier/producer_product_description_form.html:227 msgid "Close" msgstr "Fermer" #: templates/repanier/order_form.html:267 -#: templates/repanier/order_form_std.html:232 msgid "To order a bigger quantity, please contact your " msgstr "Pour commander une quantité plus grande, veuillez contacter votre " @@ -2502,15 +2175,11 @@ msgid "All" msgstr "Tous" #: templates/repanier/order_form.html:314 -#: templates/repanier/order_form_std.html:335 -#: templates/repanier/order_form_std.html:370 #: templates/repanier/permanence_form.html:117 msgid "Page " msgstr "Écran " #: templates/repanier/order_form.html:314 -#: templates/repanier/order_form_std.html:335 -#: templates/repanier/order_form_std.html:370 #: templates/repanier/permanence_form.html:117 msgid "of" msgstr "/" @@ -2533,83 +2202,57 @@ msgid "Retry4" msgstr "Veuillez réessayer (4)" #: templates/repanier/order_form.html:448 -#: templates/repanier/order_form_std.html:307 -#: templates/repanier/pre_order_form.html:139 -#: templates/repanier/widget/order_product_preview.html:20 +#: templates/repanier/pre_order_form.html:143 msgid "Communication error" msgstr "Erreur de connexion" #: templates/repanier/order_form.html:449 -#: templates/repanier/order_form_std.html:308 -#: templates/repanier/pre_order_form.html:140 +#: templates/repanier/pre_order_form.html:144 msgid "Please close this window and retry." msgstr "Veuillez fermer cette fenêtre et ressayer l\\'opération." -#: templates/repanier/order_form_std.html:165 -#: templates/repanier/order_form_std.html:178 -msgid "/ Kg" -msgstr "/kg" - -#: templates/repanier/order_form_std.html:167 -#: templates/repanier/order_form_std.html:180 -msgid "/ ℓ" -msgstr "/ℓ" - -#: templates/repanier/order_form_std.html:192 -msgid "ok" -msgstr "ok" - -#: templates/repanier/order_form_std.html:352 -msgid "All product" -msgstr "Tous les produits" - #: templates/repanier/permanence_form.html:44 msgid "No permanence found" msgstr "Les inscriptions au tableau des permanences ne sont pas ouvertes" -#: templates/repanier/pre_order_form.html:55 -#: templates/repanier/pre_order_form.html:68 -#: templates/repanier/producer_product_description_form.html:231 -#: templates/repanier/producer_product_description_form.html:235 tools.py:310 +#: templates/repanier/pre_order_form.html:59 +#: templates/repanier/pre_order_form.html:72 +#: templates/repanier/producer_product_description_form.html:230 +#: templates/repanier/producer_product_description_form.html:234 tools.py:310 #: tools.py:317 tools.py:326 msgid "pc" msgstr "pc" -#: templates/repanier/pre_order_form.html:70 -#: templates/repanier/producer_product_description_form.html:235 tools.py:313 +#: templates/repanier/pre_order_form.html:74 +#: templates/repanier/producer_product_description_form.html:234 tools.py:313 #: tools.py:320 tools.py:329 msgid "pcs" msgstr "pcs" -#: templates/repanier/producer_product_description_form.html:108 +#: templates/repanier/producer_product_description_form.html:104 msgid "1 pc = ~" msgstr "1 pc = +/-" -#: templates/repanier/producer_product_description_form.html:110 widget.py:167 +#: templates/repanier/producer_product_description_form.html:106 widget.py:167 #: widget.py:169 widget.py:195 widget.py:197 msgid "kg(s)" msgstr "kg(s)" -#: templates/repanier/producer_product_description_form.html:127 +#: templates/repanier/producer_product_description_form.html:117 msgid "w tax" msgstr "TVAC" -#: templates/repanier/producer_product_description_form.html:239 +#: templates/repanier/producer_product_description_form.html:238 msgid "Cancel" msgstr "Annuler" #: templates/repanier/send_mail_to_all_members.html:8 msgid "This message will be send to all members as coordinator." -msgstr "" -"Ce message sera envoyé à tous les membres actifs car vous êtes coordinateur/" -"trice." +msgstr "Ce message sera envoyé à tous les membres actifs car vous êtes coordinateur/trice." #: templates/repanier/send_mail_to_all_members.html:12 -msgid "" -"This message will be send only to members who accept to show they email." -msgstr "" -"Ce message sera envoyé aux seuls membres qui acceptent de montrer leur ✉ " -"mail aux autres membres." +msgid "This message will be send only to members who accept to show they email." +msgstr "Ce message sera envoyé aux seuls membres qui acceptent de montrer leur ✉ mail aux autres membres." #: templates/repanier/send_mail_to_coordinators.html:5 msgid "Which coordinator(s) have you selected ?" @@ -2617,26 +2260,22 @@ msgstr "Quel(s) membre(s) de l'équipe de gestion avez-vous sélectionné ?" #: templates/repanier/send_mail_to_coordinators.html:6 msgid "This message will be send only to coordinators you have selected." -msgstr "" -"Ce message ne sera envoyé qu'au(x) membre(s) de l'équipe de gestion que vous " -"sélectionnez ci-dessous." +msgstr "Ce message ne sera envoyé qu'au(x) membre(s) de l'équipe de gestion que vous sélectionnez ci-dessous." #: templates/repanier/who_is_who.html:5 msgid "If you want to show information about you, then" -msgstr "" -"Si vous souhaitez montrer des informations à propos de vous, alors veuillez" +msgstr "Si vous souhaitez montrer des informations à propos de vous, alors veuillez" #: templates/repanier/who_is_who.html:5 msgid "update your profil" msgstr "mettre votre profil à jour" -#: templates/repanier/who_is_who.html:13 templates/repanier/who_is_who.html:24 +#: templates/repanier/who_is_who.html:19 templates/repanier/who_is_who.html:38 msgid "Phone" msgstr "✆" #: templates/repanier/widget/order_product_preview.html:2 #: templates/repanier/widget/order_product_preview.html:3 -#: templates/repanier/widget/preview_product_order.html:3 msgid "Preview" msgstr "Prévisualiser" @@ -2678,29 +2317,27 @@ msgstr "ℓ" msgid "cl" msgstr "cℓ" -#: views.py:135 +#: views.py:136 msgid "Logged out" msgstr "Déconnecté" -#: views.py:218 +#: views.py:219 msgid "All members as coordinator" msgstr "Tous les membres en tant que coordinateur/trice" -#: views.py:220 +#: views.py:221 msgid "All members accepting to show they mail address" msgstr "Membres qui acceptent de montrer leur ✉ mail aux autres membres." -#: views.py:352 views.py:499 +#: views.py:359 views.py:512 #, python-format msgid "My balance : %(balance)s € at %(date)s" msgstr "Mon solde : %(balance)s € au %(date)s" -#: views.py:356 views.py:503 +#: views.py:363 views.py:516 #, python-format -msgid "" -"My balance : %(balance)s € at %(date)s" -msgstr "" -"Mon solde : %(balance)s € au %(date)s" +msgid "My balance : %(balance)s € at %(date)s" +msgstr "Mon solde : %(balance)s € au %(date)s" #: widget.py:160 widget.py:166 widget.py:172 widget.py:188 widget.py:194 #: widget.py:200 @@ -2724,63 +2361,63 @@ msgstr "/ kg" msgid "?" msgstr "?" -#: widget.py:300 +#: widget.py:301 msgid "Piece weight in kg" msgstr "Poids d'une pièce (en kg)" -#: widget.py:304 +#: widget.py:305 msgid "Piece content in l" msgstr "Contenu d'une pièce (en ℓ)" -#: widget.py:308 +#: widget.py:309 msgid "Number of pieces in a pack" msgstr "Nombre de pièces dans un pack" -#: widget.py:312 +#: widget.py:313 msgid "Average weight in kg of a piece" msgstr "Poids moyen (en kg) d'une pièce" -#: widget.py:320 +#: widget.py:321 msgid "Producer price for one piece" msgstr "Tarif du producteur pour 1 pièce" -#: widget.py:322 +#: widget.py:323 msgid "Customer price for one piece" msgstr "Prix de vente consommateur pour 1 pièce" -#: widget.py:326 +#: widget.py:327 msgid "Producer price for one pack" msgstr "Tarif du producteur pour 1 pack" -#: widget.py:328 +#: widget.py:329 msgid "Customer price for one pack" msgstr "Prix de vente consommateur pour 1 pack" -#: widget.py:333 +#: widget.py:334 msgid "Producer price for one kg" msgstr "Tarif du producteur pour 1 kg" -#: widget.py:335 +#: widget.py:336 msgid "Customer price for one kg" msgstr "Prix de vente consommateur pour 1 kg" -#: widget.py:339 +#: widget.py:340 msgid "Producer price for one l" msgstr "Tarif du producteur pour 1 ℓ" -#: widget.py:341 +#: widget.py:342 msgid "Customer price for one l" msgstr "Prix de vente consommateur pour 1 ℓ" -#: widget.py:345 +#: widget.py:346 msgid "Producer price for one deposit" msgstr "Prix de la consigne" -#: widget.py:349 +#: widget.py:350 msgid "Producer price for one subscription" msgstr "Montant d'une cotisation" -#: widget.py:353 +#: widget.py:354 msgid "Producer price for one transportation" msgstr "Prix d'un trajet" @@ -2788,9 +2425,9 @@ msgstr "Prix d'un trajet" msgid "Invoices" msgstr "Factures" -#: xslx/export_tools.py:88 xslx/xslx_order.py:173 xslx/xslx_product.py:42 -#: xslx/xslx_product.py:90 xslx/xslx_product.py:209 xslx/xslx_product.py:210 -#: xslx/xslx_product.py:287 xslx/xslx_product.py:295 xslx/xslx_product.py:333 +#: xslx/export_tools.py:88 xslx/xslx_order.py:173 xslx/xslx_product.py:41 +#: xslx/xslx_product.py:85 xslx/xslx_product.py:204 xslx/xslx_product.py:205 +#: xslx/xslx_product.py:282 xslx/xslx_product.py:290 xslx/xslx_product.py:328 #: xslx/xslx_purchase.py:42 xslx/xslx_purchase.py:459 #: xslx/xslx_purchase.py:463 xslx/xslx_stock.py:31 xslx/xslx_stock.py:314 msgid "Id" @@ -2821,12 +2458,8 @@ msgstr "Fichier %s importé avec succès." #: xslx/views.py:43 #, python-format -msgid "" -"Error when importing %s : File size must be <= 1 Mb and extension must be ." -"xlsx" -msgstr "" -"Erreur à l'importation de %s : La taille du fichier doit être <= 1 Mb et son " -"extension doit être .xlsx" +msgid "Error when importing %s : File size must be <= 1 Mb and extension must be .xlsx" +msgstr "Erreur à l'importation de %s : La taille du fichier doit être <= 1 Mb et son extension doit être .xlsx" #: xslx/views.py:48 msgid "No file to import." @@ -2969,55 +2602,41 @@ msgstr "Contrôle des Paniers" msgid "Customer" msgstr "Consommateur" -#: xslx/xslx_product.py:45 xslx/xslx_product.py:49 xslx/xslx_product.py:138 -#: xslx/xslx_product.py:144 +#: xslx/xslx_product.py:44 xslx/xslx_product.py:48 xslx/xslx_product.py:133 +#: xslx/xslx_product.py:139 msgid "Yes" msgstr "Oui" -#: xslx/xslx_product.py:49 xslx/xslx_product.py:95 xslx/xslx_product.py:279 -#: xslx/xslx_product.py:325 +#: xslx/xslx_product.py:48 xslx/xslx_product.py:90 xslx/xslx_product.py:274 +#: xslx/xslx_product.py:320 msgid "wrapped" msgstr "Emballé" -#: xslx/xslx_product.py:56 xslx/xslx_product.py:99 xslx/xslx_product.py:255 -#: xslx/xslx_product.py:256 +#: xslx/xslx_product.py:55 xslx/xslx_product.py:94 xslx/xslx_product.py:247 +#: xslx/xslx_product.py:248 msgid "vat or compensation" msgstr "TVA ou compensation" -#: xslx/xslx_product.py:217 -#, python-format -msgid "Row %(row_num)d : No valid departement for customer" -msgstr "" -"Ligne %(row_num)d : Le rayon renseigné n'existe pas dans la base de données." - -#: xslx/xslx_product.py:220 xslx/xslx_product.py:221 +#: xslx/xslx_product.py:212 xslx/xslx_product.py:213 msgid "producer_unit_price" msgstr "Tarif producteur à l'unité" -#: xslx/xslx_product.py:289 +#: xslx/xslx_product.py:284 #, python-format -msgid "" -"Row %(row_num)d : No id given, or the product %(producer)s - %(product)s " -"already exist." -msgstr "" -"Ligne %(row_num)d : Pas d'identifiant donné, alors que %(producer)s - " -"%(product)s existe déjà." +msgid "Row %(row_num)d : No id given, or the product %(producer)s - %(product)s already exist." +msgstr "Ligne %(row_num)d : Pas d'identifiant donné, alors que %(producer)s - %(product)s existe déjà." -#: xslx/xslx_product.py:294 xslx/xslx_product.py:332 +#: xslx/xslx_product.py:289 xslx/xslx_product.py:327 #, python-format -msgid "" -"Row %(row_num)d : The given id %(record_id)s is not the id of %(producer)s - " -"%(product)s." -msgstr "" -"Ligne %(row_num)d : L'identifiant %(record_id)s donné n'est pas " -"l'identifiant de %(producer)s - %(product)s." +msgid "Row %(row_num)d : The given id %(record_id)s is not the id of %(producer)s - %(product)s." +msgstr "Ligne %(row_num)d : L'identifiant %(record_id)s donné n'est pas l'identifiant de %(producer)s - %(product)s." -#: xslx/xslx_product.py:342 xslx/xslx_stock.py:335 +#: xslx/xslx_product.py:337 xslx/xslx_stock.py:335 #, python-format msgid "Row %(row_num)d : A required column is missing." msgstr "Ligne %(row_num)d : Une colonne requise est absente du fichier." -#: xslx/xslx_product.py:345 xslx/xslx_purchase.py:557 xslx/xslx_stock.py:338 +#: xslx/xslx_product.py:340 xslx/xslx_purchase.py:557 xslx/xslx_stock.py:338 #, python-format msgid "Row %(row_num)d : %(error_msg)s." msgstr "Ligne %(row_num)d : %(error_msg)s." @@ -3051,10 +2670,8 @@ msgstr "Ligne %(row_num)d : Pas d'achat trouvé correspondant à l'Id donné." #: xslx/xslx_purchase.py:473 #, python-format -msgid "" -"Row %(row_num)d : The given permanence doesn't own the given purchase id." -msgstr "" -"Ligne %(row_num)d : L'achat donné ne fait pas partie de la permanence donnée." +msgid "Row %(row_num)d : The given permanence doesn't own the given purchase id." +msgstr "Ligne %(row_num)d : L'achat donné ne fait pas partie de la permanence donnée." #: xslx/xslx_purchase.py:481 #, python-format @@ -3064,15 +2681,12 @@ msgstr "Ligne %(row_num)d : Producteur non reconnu." #: xslx/xslx_purchase.py:489 #, python-format msgid "Row %(row_num)d : No valid customer" -msgstr "" -"Ligne %(row_num)d : Le consommateur renseigné n'existe pas dans la base de " -"données." +msgstr "Ligne %(row_num)d : Le consommateur renseigné n'existe pas dans la base de données." #: xslx/xslx_purchase.py:553 #, python-format msgid "Row %(row_num)d : A required column is missing %(error_msg)s." -msgstr "" -"Ligne %(row_num)d : Une colonne requise est absente du fichier %(error_msg)s." +msgstr "Ligne %(row_num)d : Une colonne requise est absente du fichier %(error_msg)s." #: xslx/xslx_purchase.py:570 msgid "At least one customer must represent the buying group." @@ -3108,9 +2722,142 @@ msgstr "Contrôle du stock" #: xslx/xslx_stock.py:341 msgid "The status of this permanence prohibit you to update the stock." -msgstr "" -"L'action a été annulée car elle est incompatible avec le statut de cette " -"permanence." +msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#~ msgid "/piece (loose)" +#~ msgstr "Acheté et facturé à la pièce. → (•_•) consigne." + +#~ msgid "/piece (named)" +#~ msgstr "Acheté et facturé à la pièce. Le producteur étiquette le produit par panier. → (•_•) consigne." + +#~ msgid "/Kg (loose)" +#~ msgstr "Acheté et facturé au kg." + +#~ msgid "/Kg (named)" +#~ msgstr "Acheté et facturé au kg. Le producteur étiquette le produit par panier." + +#~ msgid "/piece -> Kg (loose)" +#~ msgstr "Acheté par pièce, facturé au kg. → (•_•) poids moyen." + +#~ msgid "/piece -> Kg (named)" +#~ msgstr "Acheté par pièce, facturé au kg. Le producteur étiquette le produit par panier. → (•_•) poids moyen." + +#~ msgid "/L (named)" +#~ msgstr "Acheté et facturé au ℓ. Le producteur étiquette le produit par panier." + +#~ msgid "/piece -> L (loose)" +#~ msgstr "Acheté par pièce, facturé au ℓ. → (•_•) contenance moyenne et consigne." + +#~ msgid "permanences" +#~ msgstr "Permanences" + +#~ msgid "Action canceled by the system. The latest bank total is in the future." +#~ msgstr "Action refusée par le système. Le solde du compte est dans le futur." + +#~ msgid "Action canceled by the system. No latest bank total has been set." +#~ msgstr "L'action a été annulée par le système. Le solde du compte n'a pas été défini." + +#~ msgid "up to" +#~ msgstr "jusqu'au" + +#~ msgid "(included)" +#~ msgstr "(inclus)" + +#~ msgid "Thanks!" +#~ msgstr "Merci !" + +#~ msgid "No bank account movement generated because there was nothing to pay." +#~ msgstr "Aucun mouvement généré car les soldes des producteurs sélectionnés sont nuls." + +#~ msgid "No bank account movement generated because there is a conflit with the latest bank total." +#~ msgstr "Aucun mouvement généré car il y a un conflit entre cette demande et le solde du compte." + +#~ msgid "The bank account movement has been generated." +#~ msgstr "Le mouvement de compte a été généré." + +#~ msgid "At least one bank account movement has not been generated because there was nothing to pay or there is a conflit with the latest bank total." +#~ msgstr "Certains mouvements n'ont pas été généré car soit il n'y avait rien à payer, soit cela aurait produit un conflit avec le solde du compte." + +#~ msgid "The bank account movement have been generated." +#~ msgstr "Les mouvements de compte ont été générés." + +#~ msgid "Please, confirm the action : generate bank account movements corresponding to the balance of each producer" +#~ msgstr "Veuillez confirmer cette action : Générer les mouvements de compte correspondants au solde des producteurs sélectionnés." + +#~ msgid "Generate bank account movements corresponding to the balances" +#~ msgstr "Générer les mouvements de compte correspondants au solde des producteurs sélectionnés" + +#~ msgid "First_name" +#~ msgstr "Prénom" + +#~ msgid "Last_name" +#~ msgstr "Nom de famille" + +#~ msgid "The product has not been duplicated because a product with the same long name already exists." +#~ msgstr "Le produit n'a pas été dupliqué car cela provoquerait la création de deux produits ayant le même nom étendu pour un même producteur." + +#~ msgid "At least one product has not been duplicated because a product with the same long name already exists." +#~ msgstr "Au moins un produit n'a pas été dupliqué car cela provoquerait la création de deux produits ayant le même nom étendu pour un même producteur." + +#~ msgid "A permanence with the same distribution date and the same short_name already exist. You must either change te distribution_date or the name." +#~ msgstr "Une permanence avec la même date de distribution et la même dénomination existe déjà. Vous devez ou bien changer la date, ou bien la dénomintation." + +#~ msgid "Export planified XLSX" +#~ msgstr "1 - Vérifier l'écran de commande avant l'ouverture" + +#~ msgid "Check" +#~ msgstr "Contrôle" + +#~ msgid "Export orders XLSX" +#~ msgstr "3 - Exporter les commandes passées" + +#~ msgid "Please, confirm the action : close and send orders" +#~ msgstr "Veuillez confirmer votre demande de clôture des commandes." + +#~ msgid "close and send orders" +#~ msgstr "4 - Clôturer les commandes et les envoyer par mail aux consommateurs et aux producteurs" + +#~ msgid "The status of this permanence prohibit you to go back to planified." +#~ msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#~ msgid "The permanence is back to planified." +#~ msgstr "La permanence est de retour au statut \"Planifié\"." + +#~ msgid "Please, confirm the action : back to planified" +#~ msgstr "Veuillez confirmer votre demande de re-planification de la permanence." + +#~ msgid "back to planified" +#~ msgstr "--- Retour au statut \"Planifié\"" + +#~ msgid "delete purchases" +#~ msgstr "⚠ - Supprimer les commandes de la permanence" + +#~ msgid "Generate 12 weekly permanences starting from this" +#~ msgstr "  - (Générer 12 permanences hebdomadaires à partir de celle-ci)" + +#~ msgid "You can only preview invoices when the permanence status is 'done'." +#~ msgstr "Vous pouvez uniquement exporter le rapport comptable des permanences dont le statut est 'commandes facturées'." + +#~ msgid "The status of this permanence prohibit you to close invoices." +#~ msgstr "L'action a été annulée car elle est incompatible avec le statut de cette permanence." + +#~ msgid "Maximum quantity" +#~ msgstr "Qté max" + +#~ msgid "/ Kg" +#~ msgstr "/kg" + +#~ msgid "/ ℓ" +#~ msgstr "/ℓ" + +#~ msgid "ok" +#~ msgstr "ok" + +#~ msgid "All product" +#~ msgstr "Tous les produits" + +#~ msgid "Row %(row_num)d : No valid departement for customer" +#~ msgstr "Ligne %(row_num)d : Le rayon renseigné n'existe pas dans la base de données." #~ msgid "Nothing to show" #~ msgstr "Rien à afficher..." @@ -3170,29 +2917,19 @@ msgstr "" #~ msgstr "Le groupe ne vous a rien acheté pour la" #~ msgid "In attachment, you will find the detail of our order for the" -#~ msgstr "" -#~ "En pièce jointe, vous trouverez le détail de la commande du groupe pour la" +#~ msgstr "En pièce jointe, vous trouverez le détail de la commande du groupe pour la" #~ msgid "WARNING: The command is present in duplicate in two separate tabs" -#~ msgstr "" -#~ "ATTENTION : La commande est présente en double, dans deux onglets " -#~ "distincts" +#~ msgstr "ATTENTION : La commande est présente en double, dans deux onglets distincts" -#~ msgid "" -#~ "Once in the order of the products to be prepared and second once in the " -#~ "order of families to prepare" -#~ msgstr "" -#~ "Une première fois dans l'ordre des produits à préparer ET une seconde " -#~ "fois dans l'ordre des paniers à préparer" +#~ msgid "Once in the order of the products to be prepared and second once in the order of families to prepare" +#~ msgstr "Une première fois dans l'ordre des produits à préparer ET une seconde fois dans l'ordre des paniers à préparer" #~ msgid "Use only one of both" -#~ msgstr "" -#~ "Veuillez n'utiliser que l'une des deux, celle qui vous convient le mieux" +#~ msgstr "Veuillez n'utiliser que l'une des deux, celle qui vous convient le mieux" #~ msgid "In case of impediment for delivering the order, please advertise me" -#~ msgstr "" -#~ "En cas d'imprévu ou d'impossibilité de nous fournir la marchandise, " -#~ "veuillez me prévenir" +#~ msgstr "En cas d'imprévu ou d'impossibilité de nous fournir la marchandise, veuillez me prévenir" #~ msgid "In attachment, you will find the detail of your order for the" #~ msgstr "En pièce jointe, vous trouverez le détail de votre commande pour la" @@ -3201,9 +2938,7 @@ msgstr "" #~ msgstr "Le montant de votre commande est de" #~ msgid "In case of impediment for keeping your basket, please advertise me" -#~ msgstr "" -#~ "En cas d'imprévu ou d'impossibilité de venir chercher votre panier en " -#~ "temps et en heure, veuillez me prévenir" +#~ msgstr "En cas d'imprévu ou d'impossibilité de venir chercher votre panier en temps et en heure, veuillez me prévenir" #~ msgid "Dear preparation team member" #~ msgstr "Cher membre de l'équipe de préparation" @@ -3215,9 +2950,7 @@ msgstr "" #~ msgstr "L'équipe de préparation est composée de" #~ msgid "In case of impediment, please advertise me" -#~ msgstr "" -#~ "En cas d'imprévu ou d'impossibilité de nous fournir la marchandise, " -#~ "veuillez me prévenir" +#~ msgstr "En cas d'imprévu ou d'impossibilité de nous fournir la marchandise, veuillez me prévenir" #~ msgid "Dear staff member" #~ msgstr "Cher membre de l'équipe de gestion" @@ -3232,21 +2965,15 @@ msgstr "" #~ msgstr "Contenu du ✉" #~ msgid "This message is send by mail to the staff with invoice summary" -#~ msgstr "" -#~ "Le récapitulatif des factures/de la livraison du groupe est en pièce " -#~ "jointe." +#~ msgstr "Le récapitulatif des factures/de la livraison du groupe est en pièce jointe." -#~ msgid "" -#~ "This message is send by mail to each customer who have an order and a " -#~ "positive balance" +#~ msgid "This message is send by mail to each customer who have an order and a positive balance" #~ msgstr "La commande du consommateur est attachée à ce mail. Mots clés : {{ " #~ msgid "order customer mail nok" #~ msgstr "Contenu du ✉" -#~ msgid "" -#~ "This message is send by mail to each customer who have an order and a " -#~ "negative balance" +#~ msgid "This message is send by mail to each customer who have an order and a negative balance" #~ msgstr "Nb : La commande est en pièces jointe" #~ msgid "order producer mail nok" @@ -3265,8 +2992,7 @@ msgstr "" #~ msgstr "Envoyer les mails uniquement à l'équipe de gestion" #~ msgid "is external group" -#~ msgstr "" -#~ "Utilisateur avec accès limité et en lecture seule à l'administration" +#~ msgstr "Utilisateur avec accès limité et en lecture seule à l'administration" #, fuzzy #~| msgid "Yes" diff --git a/repanier/management/commands/move_pictures.py b/repanier/management/commands/move_pictures.py new file mode 100644 index 0000000000000000000000000000000000000000..960fd14f06c586423304682e487df5d0598935b5 --- /dev/null +++ b/repanier/management/commands/move_pictures.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from string import upper, rsplit +from PIL import Image + +try: + from StringIO import StringIO as IO +except ImportError: + from io import BytesIO as IO +import os +from django.core.files.storage import default_storage +from django.utils.text import slugify +from django.core.management.base import BaseCommand +from django.conf import settings +from repanier.models import LUT_ProductionMode, LUT_DeliveryPoint, LUT_DepartmentForCustomer, LUT_PermanenceRole, \ + Product, OfferItem + +# sudo rm -rf /var/tmp/django_cache/ptidej.repanier.be/ +# cd /home/pi/v2/ptidej/ptidej/media/ +# sudo chown pi:pi public +# cd /home/pi/v2/ptidej/ +# python manage.py move_pictures +# cd /home/pi/v2/ptidej/ptidej/media/ +# sudo chown -R www-data:www-data public +# sudo chown www-data:www-data /var/tmp/django_cache/ptidej.repanier.be/ + +# do not forget to rename picture 2 in picture, makemigrations and in admin picture_field.widget.upload_to += os_sep + producer.short_profile_name + +class Command(BaseCommand): + args = '' + help = 'Recalculate order amount' + + def handle(self, *args, **options): + + for obj in LUT_ProductionMode.objects.all(): + if obj.picture is not None: + self.move(record=obj, to_subdir="label", size="XS") + # for obj in LUT_DeliveryPoint.objects.all(): + # if obj.picture is not None: + # self.move(record=obj, to_subdir="LUT_DeliveryPoint", size="S") + # for obj in LUT_DepartmentForCustomer.objects.all(): + # if obj.picture is not None: + # self.move(record=obj, to_subdir="LUT_DepartmentForCustomer", size="S") + # for obj in LUT_PermanenceRole.objects.all(): + # if obj.picture is not None: + # self.move(record=obj, to_subdir="LUT_PermanenceRole", size="S") + for product in Product.objects.all(): + if product.picture is not None: + self.move(record=product, to_subdir="product" + os.sep + str(product.producer.id), size="M") + for obj in OfferItem.objects.filter(product_id=product.id).order_by(): + obj.picture2 = product.picture2 + obj.save() + + def move(self, record=None, to_subdir=None, size="M"): + directory = "%s/%s" % (settings.MEDIA_ROOT, to_subdir) + if not os.path.exists(directory): + os.makedirs(directory, 0755) + file_ = record.picture.file_ptr.file + file_name, extension = os.path.splitext(rsplit(file_.name, os.sep, 1)[1]) + safe_name = '{0}{1}'.format(slugify(file_name), extension) + name = os.path.join(to_subdir, safe_name) + if default_storage.exists(name): + default_storage.delete(name) + image = Image.open(file_) + if size == "XS": + size = (48, 48) + path = self.resize(image, name, size) + else: + # size = "M" + size = (225, 225) + path = self.resize(image, name, size) + record.picture2 = path + record.save() + + def resize(image, name, size): + if image.size[0] > size[0] or image.size[1] > size[1]: + new_mode = 'RGB' + if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info): + # image is transparent + if image.mode != 'RGBA': + image = image.convert('RGBA') + base = Image.new('RGBA', image.size, '#fff') + base.paste(image, mask=image) + image = base + if image.mode != new_mode: + image = image.convert(new_mode) + image.thumbnail(size, resample=Image.ANTIALIAS) + image_resized = IO() + image.save(image_resized, 'jpeg') + path = default_storage.save(name, image_resized) + else: + path = default_storage.save(name, image.fp) + return path diff --git a/repanier/management/commands/recalculate_order_amount.py b/repanier/management/commands/recalculate_order_amount.py index fe94dfedac8b027a3a04e07e3541784d53ba7a48..f9694ad1d1ec7a6a3592c60be3a3a374092b9465 100644 --- a/repanier/management/commands/recalculate_order_amount.py +++ b/repanier/management/commands/recalculate_order_amount.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django.core.management.base import BaseCommand from repanier.const import * -from repanier.models import Permanence, OfferItem +from repanier.models import Permanence from repanier.tools import recalculate_order_amount diff --git a/repanier/menu.py b/repanier/menu.py index fb93aae8194e45c19971f2684cc069660a58f821..84ae39cee07ab66a21476e7f6205f4b3102a026b 100644 --- a/repanier/menu.py +++ b/repanier/menu.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import datetime from django.conf import settings -from models import repanier_settings +from apps import RepanierSettings from django.utils.formats import number_format from menus.base import Menu, NavigationNode from menus.menu_pool import menu_pool @@ -24,7 +24,7 @@ class PermanenceMenu(Menu): # if request.user.is_authenticated(): master_id = 2 node = NavigationNode( - "%s" % repanier_settings['PERMANENCE_NAME'], + "%s" % RepanierSettings.permanence_name, "/", id=master_id, visible=True @@ -61,7 +61,7 @@ class PermanenceMenu(Menu): nodes.append(node) submenu_id += 1 first_pass = False - if repanier_settings['DISPLAY_ANONYMOUS_ORDER_FORM']: + if RepanierSettings.display_anonymous_order_form: path = reverse('basket_view', args=(permanence.id,)) else: path = reverse('order_view', args=(permanence.id,)) diff --git a/repanier/models.py b/repanier/models.py index 63b91bf3dae71d779df052e4b056d0538ff8ab42..70bcfbde0cff15f3618be76b0833b3749fc26e48 100644 --- a/repanier/models.py +++ b/repanier/models.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 from __future__ import unicode_literals +from picture.const import SIZE_XS, SIZE_M, SIZE_S +from picture.fields import AjaxPictureField from cms.toolbar_pool import toolbar_pool from django.contrib.sites.models import Site from django.core.validators import MinLengthValidator @@ -9,7 +11,7 @@ from django.db.models import F from const import * from django.conf import settings -from apps import repanier_settings +from apps import RepanierSettings from django.db import models from django.db import transaction from django.db.models.signals import pre_save @@ -192,49 +194,49 @@ def configuration_post_save(sender, **kwargs): config = kwargs['instance'] if config.id is not None: - repanier_settings['CONFIG'] = config + RepanierSettings.config = config site = Site.objects.get_current() if site is not None: site.name = config.group_name site.domain = settings.ALLOWED_HOSTS[0] site.save() - repanier_settings['GROUP_NAME'] = config.group_name + RepanierSettings.group_name = config.group_name if config.name == PERMANENCE_NAME_PERMANENCE: - repanier_settings['PERMANENCE_NAME'] = _("Permanence") - repanier_settings['PERMANENCES_NAME'] = _("Permanences") - repanier_settings['PERMANENCE_ON_NAME'] = _("Permanence on ") + RepanierSettings.permanence_name = _("Permanence") + RepanierSettings.permanences_name = _("Permanences") + RepanierSettings.permanence_on_name = _("Permanence on ") elif config.name == PERMANENCE_NAME_CLOSURE: - repanier_settings['PERMANENCE_NAME'] = _("Closure") - repanier_settings['PERMANENCES_NAME'] = _("Closures") - repanier_settings['PERMANENCE_ON_NAME'] = _("Closure on ") + RepanierSettings.permanence_name = _("Closure") + RepanierSettings.permanences_name = _("Closures") + RepanierSettings.permanence_on_name = _("Closure on ") elif config.name == PERMANENCE_NAME_DELIVERY: - repanier_settings['PERMANENCE_NAME'] = _("Delivery") - repanier_settings['PERMANENCES_NAME'] = _("Deliveries") - repanier_settings['PERMANENCE_ON_NAME'] = _("Delivery on ") + RepanierSettings.permanence_name = _("Delivery") + RepanierSettings.permanences_name = _("Deliveries") + RepanierSettings.permanence_on_name = _("Delivery on ") else: - # PERMANENCE_NAME_ORDER - repanier_settings['PERMANENCE_NAME'] = _("Order") - repanier_settings['PERMANENCES_NAME'] = _("Orders") - repanier_settings['PERMANENCE_ON_NAME'] = _("Order on ") - repanier_settings['TEST_MODE'] = config.test_mode - repanier_settings['MAX_WEEK_WO_PARTICIPATION'] = config.max_week_wo_participation - repanier_settings['SEND_OPENING_MAIL_TO_CUSTOMER'] = config.send_opening_mail_to_customer - repanier_settings['SEND_ORDER_MAIL_TO_CUSTOMER'] = config.send_order_mail_to_customer - repanier_settings['SEND_ORDER_MAIL_TO_PRODUCER'] = config.send_order_mail_to_producer - repanier_settings['SEND_ORDER_MAIL_TO_BOARD'] = config.send_order_mail_to_board - repanier_settings['SEND_INVOICE_MAIL_TO_CUSTOMER'] = config.send_invoice_mail_to_customer - repanier_settings['SEND_INVOICE_MAIL_TO_PRODUCER'] = config.send_invoice_mail_to_producer - repanier_settings['INVOICE'] = config.invoice - repanier_settings['STOCK'] = config.stock - repanier_settings['DISPLAY_ANONYMOUS_ORDER_FORM'] = config.display_anonymous_order_form - repanier_settings['DISPLAY_PRODUCERS_ON_ORDER_FORM'] = config.display_producer_on_order_form - repanier_settings['BANK_ACCOUNT'] = config.bank_account - repanier_settings['PRODUCER_ORDER_ROUNDED'] = config.producer_order_rounded - repanier_settings['PRODUCER_PRE_OPENING'] = config.producer_pre_opening - repanier_settings['ACCEPT_CHILD_GROUP'] = config.accept_child_group - repanier_settings['DELIVERY_POINT'] = config.delivery_point - repanier_settings['DISPLAY_VAT'] = config.display_vat - repanier_settings['VAT_ID'] = config.vat_id + RepanierSettings.permanence_name = _("Order") + RepanierSettings.permanences_name = _("Orders") + RepanierSettings.permanence_on_name = _("Order on ") + RepanierSettings.test_mode = config.test_mode + RepanierSettings.max_week_wo_participation = config.max_week_wo_participation + RepanierSettings.send_opening_mail_to_customer = config.send_opening_mail_to_customer + RepanierSettings.send_order_mail_to_customer = config.send_order_mail_to_customer + RepanierSettings.send_order_mail_to_producer = config.send_order_mail_to_producer + RepanierSettings.send_order_mail_to_board = config.send_order_mail_to_board + RepanierSettings.send_invoice_mail_to_customer = config.send_invoice_mail_to_customer + RepanierSettings.send_invoice_mail_to_producer = config.send_invoice_mail_to_producer + RepanierSettings.invoice = config.invoice + RepanierSettings.stock = config.stock + RepanierSettings.display_anonymous_order_form = config.display_anonymous_order_form + RepanierSettings.display_producer_on_order_form = config.display_producer_on_order_form + RepanierSettings.bank_account = config.bank_account + RepanierSettings.producer_order_rounded = config.producer_order_rounded + RepanierSettings.producer_pre_opening = config.producer_pre_opening + RepanierSettings.accept_child_group = config.accept_child_group + RepanierSettings.delivery_point = config.delivery_point + RepanierSettings.display_vat = config.display_vat + RepanierSettings.vat_id = config.vat_id + RepanierSettings.page_break_on_customer_check = config.page_break_on_customer_check if not config.stock: Producer.objects.all().update(manage_stock=False) @@ -263,6 +265,11 @@ class LUT_ProductionMode(MPTTModel, TranslatableModel): picture = FilerImageField( verbose_name=_("picture"), related_name="production_mode_picture", null=True, blank=True) + picture2 = AjaxPictureField( + verbose_name=_("picture"), + null=True, blank=True, + upload_to="label", size=SIZE_XS) + is_active = models.BooleanField(_("is_active"), default=True) objects = LUT_ProductionModeManager() @@ -289,9 +296,6 @@ class LUT_DeliveryPoint(MPTTModel, TranslatableModel): short_name=models.CharField(_("short_name"), max_length=50, db_index=True, unique=True, default=EMPTY_STRING), description=HTMLField(_("description"), blank=True, default=EMPTY_STRING), ) - picture = FilerImageField( - verbose_name=_("picture"), related_name="delivery_point_picture", - null=True, blank=True) is_active = models.BooleanField(_("is_active"), default=True) objects = LUT_DeliveryPointManager() @@ -318,9 +322,6 @@ class LUT_DepartmentForCustomer(MPTTModel, TranslatableModel): short_name=models.CharField(_("short_name"), max_length=50, db_index=True, unique=True, default=EMPTY_STRING), description=HTMLField(_("description"), blank=True, default=EMPTY_STRING), ) - picture = FilerImageField( - verbose_name=_("picture"), related_name="department_picture", - null=True, blank=True) is_active = models.BooleanField(_("is_active"), default=True) objects = LUT_ProductionModeManager() @@ -347,11 +348,8 @@ class LUT_PermanenceRole(MPTTModel, TranslatableModel): short_name=models.CharField(_("short_name"), max_length=50, db_index=True, unique=True, default=EMPTY_STRING), description=HTMLField(_("description"), blank=True, default=EMPTY_STRING), ) - picture = FilerImageField( - verbose_name=_("picture"), related_name="permanence_role_picture", - null=True, blank=True) + is_active = models.BooleanField(_("is_active"), default=True) - # automatically_added = models.BooleanField(_("automatically added to the new permanences"), default=False) objects = LUT_ProductionModeManager() def __str__(self): @@ -417,7 +415,7 @@ class Producer(models.Model): verbose_name=_("Default tax")) date_balance = models.DateField( - _("date_balance"), default=datetime.date.today()) + _("date_balance"), default=datetime.date.today) balance = models.DecimalField( _("balance"), max_digits=8, decimal_places=2, default=DECIMAL_ZERO) # The initial balance is needed to compute the invoice control list @@ -569,7 +567,10 @@ class Customer(models.Model): LUT_DeliveryPoint, verbose_name=_("delivery point"), null=True, blank=True, default=None, on_delete=models.PROTECT) - + picture = AjaxPictureField( + verbose_name=_("picture"), + null=True, blank=True, + upload_to="customer", size=SIZE_S) phone1 = models.CharField( _("phone1"), max_length=25, @@ -593,7 +594,7 @@ class Customer(models.Model): password_reset_on = models.DateTimeField( _("password_reset_on"), null=True, blank=True, default=None) date_balance = models.DateField( - _("date_balance"), default=datetime.date.today()) + _("date_balance"), default=datetime.date.today) balance = models.DecimalField( _("balance"), max_digits=8, decimal_places=2, default=DECIMAL_ZERO) # The initial balance is needed to compute the invoice control list @@ -716,6 +717,7 @@ class Staff(models.Model): password_reset_on = models.DateTimeField( _("password_reset_on"), null=True, blank=True, default=None) is_active = models.BooleanField(_("is_active"), default=True) + is_active = models.BooleanField(_("is_active"), default=True) def natural_key(self): return self.user.natural_key() @@ -781,11 +783,16 @@ class Product(TranslatableModel): picture = FilerImageField( verbose_name=_("picture"), related_name="product_picture", null=True, blank=True) + picture2 = AjaxPictureField( + verbose_name=_("picture"), + null=True, blank=True, + upload_to="product", size=SIZE_M) + reference = models.CharField( _("reference"), max_length=36, blank=True, null=True) department_for_customer = models.ForeignKey( - LUT_DepartmentForCustomer, + LUT_DepartmentForCustomer, null=True, blank=True, verbose_name=_("department_for_customer"), on_delete=models.PROTECT) @@ -818,7 +825,7 @@ class Product(TranslatableModel): compensation = models.DecimalField( _("compensation"), help_text=_("compensation to add to the customer unit price"), - default=DECIMAL_ZERO, max_digits=8, decimal_places=4) + default=DECIMAL_ZERO, max_digits=8, decimal_places=4) unit_deposit = models.DecimalField( _("deposit"), help_text=_('deposit to add to the original unit price'), @@ -1062,7 +1069,9 @@ class Permanence(TranslatableModel): def get_producers(self): if self.id is not None: if len(self.producers.all()) > 0: - if self.status == PERMANENCE_PLANNED: + if self.status < PERMANENCE_OPENED: + return ", ".join([p.short_profile_name for p in self.producers.all()]) + elif self.status == PERMANENCE_OPENED: changelist_url = urlresolvers.reverse( 'admin:repanier_product_changelist', ) @@ -1139,9 +1148,9 @@ class Permanence(TranslatableModel): def get_customers(self): if self.id is not None: customers = "" - if self.status == PERMANENCE_OPENED: + if self.status in [PERMANENCE_OPENED, PERMANENCE_CLOSED]: changelist_url = urlresolvers.reverse( - 'admin:repanier_purchaseopenedforupdate_changelist', + 'admin:repanier_purchaseopenedorclosedforupdate_changelist', ) customers += ", ".join([' 0 and ((disk.f_bfree + 1.0) / disk.f_blocks) < 0.2: + data = json.dumps({'error': _not_lazy( + 'Please, contact the administrator of the webserver : There is not enough disk space.')}) + return HttpResponse(data, content_type="application/json", status=403) + file_ = form.cleaned_data['file'] + + image_types = ['image/png', 'image/jpg', 'image/jpeg', 'image/pjpeg', + 'image/gif'] + + if file_.content_type not in image_types: + data = json.dumps({'error': _not_lazy('The system does not recognize the format.')}) + return HttpResponse(data, content_type="application/json", status=403) + + file_name, extension = os.path.splitext(file_.name) + safe_name = '{0}{1}'.format(slugify(file_name), extension) + # name = os.path.join(upload_to or "tmp", str(request.user.id), safe_name) + name = os.path.join(upload_to or "tmp", safe_name) + + if default_storage.exists(name): + return HttpResponse( + json.dumps( + {'url': default_storage.url(name), + 'filename': name, + 'msg': _not_lazy( + 'An image with same file name already exist. Please, check the file name and rename it if necessary.') + } + ), content_type="application/json") + else: + image = Image.open(file_) + if image.size[0] > size or image.size[1] > size: + data = json.dumps({'error': _not_lazy('Wrong size.')}) + return HttpResponse(data, content_type="application/json", status=403) + + file_name = default_storage.save(name, image.fp) + url = default_storage.url(file_name) + + return HttpResponse(json.dumps({'url': url, 'filename': file_name}), content_type="application/json") + + return HttpResponse(status=403) diff --git a/repanier/task/task_invoice.py b/repanier/task/task_invoice.py index 2e7058ca0ca02218e44435a25fc8291d43bff69f..1de3c8478441c68dd62e225e9d286925b819b028 100644 --- a/repanier/task/task_invoice.py +++ b/repanier/task/task_invoice.py @@ -11,7 +11,6 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from menus.menu_pool import menu_pool from repanier.const import * -from repanier.models import repanier_settings from repanier.models import BankAccount from repanier.models import Customer from repanier.models import CustomerInvoice @@ -30,9 +29,10 @@ import thread @transaction.atomic def generate(request, permanence, payment_date, producers_to_be_paid_set): - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: validation_passed = True getcontext().rounding=ROUND_HALF_UP + new_bank_latest_total = DECIMAL_ZERO bank_account = BankAccount.objects.filter(operation_status=BANK_LATEST_TOTAL).order_by().first() if bank_account is None: # If not latest total exists, create it with operation date before all movements @@ -51,20 +51,20 @@ def generate(request, permanence, payment_date, producers_to_be_paid_set): producer_buyinggroup = Producer.objects.filter(is_active=True, represent_this_buyinggroup=True).order_by().first() if producer_buyinggroup is None: producer_buyinggroup = Producer.objects.create( - short_profile_name="z-%s" % repanier_settings['GROUP_NAME'], - long_profile_name=repanier_settings['GROUP_NAME'], + short_profile_name="z-%s" % RepanierSettings.group_name, + long_profile_name=RepanierSettings.group_name, represent_this_buyinggroup=True ) if producer_buyinggroup is not None: customer_buyinggroup = Customer.objects.filter(is_active=True, represent_this_buyinggroup=True).order_by().first() if customer_buyinggroup is None: user = User.objects.create_user( - username="z-%s" % repanier_settings['GROUP_NAME'], email=None, password=uuid.uuid1().hex, - first_name="", last_name=repanier_settings['GROUP_NAME']) + username="z-%s" % RepanierSettings.group_name, email=None, password=uuid.uuid1().hex, + first_name="", last_name=RepanierSettings.group_name) customer_buyinggroup = Customer.objects.create( user=user, - short_basket_name="z-%s" % repanier_settings['GROUP_NAME'], - long_basket_name=repanier_settings['GROUP_NAME'], + short_basket_name="z-%s" % RepanierSettings.group_name, + long_basket_name=RepanierSettings.group_name, represent_this_buyinggroup=True ) @@ -566,7 +566,7 @@ def admin_generate(request, producers_to_be_paid_set=Producer.objects.none(), pe def admin_send(request, queryset): - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: user_message = _("The status of this permanence prohibit you to send invoices.") user_message_level = messages.ERROR for permanence in queryset[:1]: @@ -584,7 +584,7 @@ def admin_send(request, queryset): def admin_cancel(request, queryset): user_message = _("The status of this permanence prohibit you to cancel invoices.") user_message_level = messages.ERROR - if repanier_settings['INVOICE']: + if RepanierSettings.invoice: latest_total = BankAccount.objects.filter( operation_status=BANK_LATEST_TOTAL).only( "permanence" diff --git a/repanier/task/task_order.py b/repanier/task/task_order.py index 983dddf761608471d8f1347b2a872cac13a93093..51b2ed8f14b33281184412f0f4f5f6f4a689bc4f 100644 --- a/repanier/task/task_order.py +++ b/repanier/task/task_order.py @@ -8,8 +8,8 @@ from django.utils import timezone from django.utils import translation from django.utils.translation import ugettext_lazy as _ from menus.menu_pool import menu_pool +from repanier.apps import RepanierSettings from repanier.const import * -from repanier.models import repanier_settings from repanier.email import email_alert from repanier.email import email_offer from repanier.email import email_order @@ -96,8 +96,7 @@ def common_to_pre_open_and_open(permanence_id): @transaction.atomic def pre_open(permanence_id): permanence = common_to_pre_open_and_open(permanence_id) - if repanier_settings['PRODUCER_PRE_OPENING']: - email_offer.send_pre_opening(permanence_id) + email_offer.send_pre_opening(permanence_id) now = timezone.now() permanence.status = PERMANENCE_PRE_OPEN if permanence.highest_status < PERMANENCE_PRE_OPEN: @@ -110,7 +109,7 @@ def pre_open(permanence_id): @transaction.atomic def open(permanence_id): permanence = common_to_pre_open_and_open(permanence_id) - if repanier_settings['SEND_OPENING_MAIL_TO_CUSTOMER']: + if RepanierSettings.send_opening_mail_to_customer: email_offer.send(permanence_id) now = timezone.now() permanence.status = PERMANENCE_OPENED @@ -191,7 +190,7 @@ def admin_open_and_send(request, queryset): now = timezone.now() for permanence in queryset[:1]: if permanence.status in [PERMANENCE_PLANNED, PERMANENCE_PRE_OPEN]: - if repanier_settings['PRODUCER_PRE_OPENING'] and permanence.status == PERMANENCE_PLANNED: + if RepanierSettings.producer_pre_opening and permanence.status == PERMANENCE_PLANNED: permanence_already_pre_opened = Permanence.objects.filter( status__in=[PERMANENCE_WAIT_FOR_PRE_OPEN, PERMANENCE_PRE_OPEN] ).order_by("-is_updated_on").only("id").first() @@ -225,7 +224,7 @@ def admin_open_and_send(request, queryset): if permanence.status == PERMANENCE_WAIT_FOR_PRE_OPEN: permanence.status = PERMANENCE_PLANNED else: - if repanier_settings['PRODUCER_PRE_OPENING']: + if RepanierSettings.producer_pre_opening: permanence.status = PERMANENCE_PRE_OPEN else: permanence.status = PERMANENCE_PLANNED @@ -327,7 +326,7 @@ def close(permanence_id): now = timezone.now() permanence.is_updated_on = now permanence.save(update_fields=['status', 'is_updated_on', 'highest_status']) - if not repanier_settings['INVOICE']: + if not RepanierSettings.invoice: # Put send permanences to the done status, because they will "never" be invoiced for permanence in Permanence.objects.filter(status=PERMANENCE_SEND): permanence.status = PERMANENCE_DONE diff --git a/repanier/templates/repanier/cache_part_a.html b/repanier/templates/repanier/cache_part_a.html index 77b7aa820104b4a0200af5f4ec380711b0e08b5d..6c0039807146c9784529586463c7c05ae4a6d716 100644 --- a/repanier/templates/repanier/cache_part_a.html +++ b/repanier/templates/repanier/cache_part_a.html @@ -1,12 +1,12 @@ -{% load cms_tags sekizai_tags i18n l10n repanier_tags thumbnail filer_tags filer_image_tags %} +{% load cms_tags sekizai_tags i18n l10n repanier_tags %} {{ offer.get_long_name }}
{{ offer.producer.short_profile_name }} {% if offer.product.offer_description|length > 0 %} {% endif %}
{{ offer.department_for_customer.short_name }}
{% for production_mode in offer.product.production_mode.all %} - {% if production_mode.picture %} - {% thumbnail production_mode.picture "0x32"|extra_padding_y:5 crop="smart" as logo_thumbnail %} - {{ production_mode }} + {% if production_mode.picture2 %} + {{ production_mode }} {% else %} {{ production_mode }}{% if not forloop.last %} - {% endif %} {% endif %} diff --git a/repanier/templates/repanier/cache_part_b.html b/repanier/templates/repanier/cache_part_b.html index 9b7db60d513299ff9a59035ed049ade7acea4609..5265ea70c6803522112b420ee2cbeef9d812d46f 100644 --- a/repanier/templates/repanier/cache_part_b.html +++ b/repanier/templates/repanier/cache_part_b.html @@ -1,4 +1,4 @@ -{% load cms_tags sekizai_tags i18n l10n repanier_tags thumbnail filer_tags filer_image_tags %} +{% load cms_tags sekizai_tags i18n l10n repanier_tags %} {% if offer.order_unit == "105" or offer.order_unit == "110" or offer.order_unit == "115" %}
{{ offer.reference_price_with_vat }} € @@ -18,10 +18,11 @@
{{ offer.unit_deposit }} € {% endif %} -{% if offer.product.picture %} - {% thumbnail offer.product.picture "0x150"|extra_padding_y:5 crop="smart" as product_thumbnail %} +{% if offer.picture2 %}
- - {{ offer.product }} + + {{ offer.product }} {% endif %} \ No newline at end of file diff --git a/repanier/templates/repanier/cache_part_c.html b/repanier/templates/repanier/cache_part_c.html index 611d69803467b359535f39ab9e27bf97ca48329e..7b44782f2891f7bb7bae793d7592fd4e3f725ec0 100644 --- a/repanier/templates/repanier/cache_part_c.html +++ b/repanier/templates/repanier/cache_part_c.html @@ -1,4 +1,4 @@ -{% load cms_tags sekizai_tags i18n l10n repanier_tags thumbnail filer_tags filer_image_tags %} +{% load cms_tags sekizai_tags i18n l10n repanier_tags %} {% if offer.order_unit == "105" or offer.order_unit == "110" or offer.order_unit == "115" %}
{{ offer.reference_price_with_compensation }} € @@ -18,10 +18,11 @@
{{ offer.unit_deposit }} € {% endif %} -{% if offer.product.picture %} - {% thumbnail offer.product.picture "0x150"|extra_padding_y:5 crop="smart" as product_thumbnail %} +{% if offer.picture2 %}
- - {{ offer.product }} + + {{ offer.product }} {% endif %} \ No newline at end of file diff --git a/repanier/templates/repanier/cache_part_e.html b/repanier/templates/repanier/cache_part_e.html index 99e9a3014975bdcbe25e8fc6718fa16355902d88..5e59810cf13f54e9fcbed72977e6f6668425c3c3 100644 --- a/repanier/templates/repanier/cache_part_e.html +++ b/repanier/templates/repanier/cache_part_e.html @@ -1,9 +1,9 @@ -{% load cms_tags sekizai_tags i18n l10n repanier_tags thumbnail filer_tags filer_image_tags %} +{% load cms_tags sekizai_tags i18n l10n repanier_tags %} {{ offer.producer.short_profile_name }}
{{ offer.department_for_customer.short_name }}
-{% if offer.picture %} - {% thumbnail offer.picture "0x150"|extra_padding_y:5 crop="smart" as product_thumbnail %} - {{ offer.get_long_name }} +{% if offer.picture2 %} + {{ offer.get_long_name }} {% endif %} {% if offer.product.offer_description %} {{ offer.product.offer_description | safe }} @@ -12,12 +12,16 @@ {% endif %}
{% for production_mode in offer.product.production_mode.all %} - {% if production_mode.picture %} - {% thumbnail production_mode.picture "0x32"|extra_padding_y:5 crop="smart" as logo_thumbnail %} - {{ production_mode }} + {% if production_mode.picture2 %} + {{ production_mode }} + {% if production_mode.description %}
+ {{ production_mode.description | safe }}{% if not forloop.last %}
{% endif %}{% endif %} {% else %} {{ production_mode }} + {% if production_mode.description %}
+ {{ production_mode.description | safe }}{% if not forloop.last %}
{% endif %}{% else %} + {% if not forloop.last %} + -{% endif %}{% endif %} {% endif %} - {% if production_mode.description %}
{{ production_mode.description | safe }}{% if not forloop.last %}
{% endif %}{% endif %} - -{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/repanier/templates/repanier/logged_out.html b/repanier/templates/repanier/logged_out.html index d77ca4de2d1e8990ebc18ef07489a9ddc24085cc..327b331cd96e8cde567e70fcd7c9f7334262c1dd 100644 --- a/repanier/templates/repanier/logged_out.html +++ b/repanier/templates/repanier/logged_out.html @@ -7,7 +7,9 @@
diff --git a/repanier/templates/repanier/me_form.html b/repanier/templates/repanier/me_form.html index 7ac39a1824294903813b86701e02a6eb907a24fe..aae5201ad047feb446f0cf52acfa6323b8f670f0 100644 --- a/repanier/templates/repanier/me_form.html +++ b/repanier/templates/repanier/me_form.html @@ -1,5 +1,9 @@ {% extends 'subpage_base.html' %} {% load cms_tags sekizai_tags i18n l10n crispy_forms_tags %} +{% addtoblock "jshead" %} + {% endaddtoblock %} {% block sub_content %}
{% if update %}

{% trans "Update done." %}

{% endif %} diff --git a/repanier/templates/repanier/order_form.html b/repanier/templates/repanier/order_form.html index 60116ed1b661fa129cd65f29dcd5e209e1ab4bb4..ad33b7b2de4214c6f1012692f3653867aa0b4553 100644 --- a/repanier/templates/repanier/order_form.html +++ b/repanier/templates/repanier/order_form.html @@ -94,9 +94,10 @@ body > .container { {# {% csrf_token %}#}
{% if q != None %} - + {% else %} - + {% endif %} diff --git a/repanier/templates/repanier/pre_order_form.html b/repanier/templates/repanier/pre_order_form.html index c25b344768ebe7a0bbde0e8b1e946ba8ff1c914e..25f35565932a84576cc8cd62d43431bc6378b61c 100644 --- a/repanier/templates/repanier/pre_order_form.html +++ b/repanier/templates/repanier/pre_order_form.html @@ -49,7 +49,13 @@ body > .container { {{ offer_item.producer }} - {{ offer_item.get_long_name | safe }} + {{ offer_item.get_long_name | safe }} + {% if offer_item.picture2 %} + + {% endif %} + {{ offer_item.producer_unit_price_wo_tax|floatformat:-2 }} € / {% if offer_item.order_unit == '115' %}({% trans "pc" %}){% else %}({% trans "kg" %}){% endif %} @@ -100,7 +106,7 @@ body > .container { {# Order modal ends#} {% addtoblock "lastjs" %} {% endaddtoblock %} diff --git a/repanier/templates/repanier/producer_product_description_form.html b/repanier/templates/repanier/producer_product_description_form.html index df1611e8c9eb656e95e804f8c11449224fcc49e3..d49610f7a5dba88e70ff740f23220bc8105370bc 100644 --- a/repanier/templates/repanier/producer_product_description_form.html +++ b/repanier/templates/repanier/producer_product_description_form.html @@ -1,5 +1,4 @@ {% load cms_tags sekizai_tags i18n l10n crispy_forms_tags %} -{#
#}
{% endwith %}
@@ -109,13 +105,7 @@
{% trans "kg(s)" %}
-
-{# {{ form.customer_increment_order_quantity.errors }}#} -{# #} -
-
+
{% endwith %}
@@ -177,68 +167,92 @@
{% with id_for_label=form.offer_description.id_for_label %}
- - {% if form.offer_description.errors %} -

- {% for error in form.offer_description.errors %} - {{ error }} - {% endfor %} -

- {% endif %} -
-
- - - - +
+ + {% if form.offer_description.errors %} +

+ {% for error in form.offer_description.errors %} + {{ error }} + {% endfor %} +

+ {% endif %} + {% if form.picture.errors %} +

+ {% for error in form.picture.errors %} + {{ error }} + {% endfor %} +

+ {% endif %} +
+ {{ form.picture }} +
+
+
+ + + + +
{% endwith %}
-{#
#} -{#
#} \ No newline at end of file + \ No newline at end of file diff --git a/repanier/templates/repanier/who_is_who.html b/repanier/templates/repanier/who_is_who.html index 13d48702225f00d482c4e04750379831b335dd35..0a5f0dac1e5b661e287d8b2f104f05563b9e24bb 100644 --- a/repanier/templates/repanier/who_is_who.html +++ b/repanier/templates/repanier/who_is_who.html @@ -6,24 +6,42 @@ {% if coordinator %} {% for customer in customer_list %}

+

+ {% if customer.picture %} + {{ customer.long_basket_name }} + {% endif %} +
+
{{ customer.long_basket_name }} {% if customer.long_basket_name != customer.short_basket_name %} ({{customer.short_basket_name }}){% endif %} {% if customer.city %}
{{ customer.city }}{% endif %} {% if customer.memo %}
{{ customer.memo }}{% endif %}
{% trans "Phone" %} : {{ customer.phone1 }}{% if customer.phone2 %}, {{ customer.phone2 }}{% endif %}
{% trans "Email" %} : {{ customer.user.email }}{% if customer.email2 %}, {{ customer.email2 }}{% endif %} +

+
{% endfor %} {% else %} {% for customer in customer_list %}

+

+ {% if customer.picture %} + {{ customer.long_basket_name }} + {% endif %} +
+
{{ customer.long_basket_name }} {# {% if customer.long_basket_name != customer.short_basket_name %} ({{customer.short_basket_name }}){% endif %}#} {% if customer.city %}
{{ customer.city }}{% endif %} {% if customer.memo %}
{{ customer.memo | safe }}{% endif %} {% if customer.accept_phone_call_from_members %}
{% trans "Phone" %} : {{ customer.phone1 }}{% if customer.phone2 %}, {{ customer.phone2 }}{% endif %}{% endif %} {% if customer.accept_mails_from_members %}
{% trans "Email" %} : {{ customer.user.email }}{% if customer.email2 %}, {{ customer.email2 }}{% endif %}{% endif %} +

+
{% endfor %} {% endif %} diff --git a/repanier/templates/repanier/widget/order_product_preview.html b/repanier/templates/repanier/widget/order_product_preview.html index c0204dfc2004691618d4b3ac74effaa9fe1fb5f2..68570eb107f6e832f4cf9f4c162070fb6812fc23 100644 --- a/repanier/templates/repanier/widget/order_product_preview.html +++ b/repanier/templates/repanier/widget/order_product_preview.html @@ -3,22 +3,22 @@ {% trans "Preview" %} - \ No newline at end of file +{##} \ No newline at end of file diff --git a/repanier/tools.py b/repanier/tools.py index 03abc8b4707a1b8524e943ef2f3ad993512793e7..79d58e04960148b10d3c6764953784baee40e060 100644 --- a/repanier/tools.py +++ b/repanier/tools.py @@ -2,10 +2,10 @@ from __future__ import unicode_literals from django.template.loader import render_to_string from django.utils import translation +from apps import RepanierSettings from const import * from django.utils.translation import ugettext_lazy as _ from django.conf import settings -from models import repanier_settings from django.utils.formats import number_format from django.db import transaction import models @@ -55,7 +55,7 @@ def get_allowed_mail_extension(): def send_email(email=None): if settings.DEBUG: - if repanier_settings["TEST_MODE"]: + if RepanierSettings.test_mode: coordinator = models.Staff.objects.filter(is_coordinator=True, is_active=True).order_by().first() if coordinator is not None: email.to = [coordinator.user.email] @@ -66,7 +66,7 @@ def send_email(email=None): email.send() else: pass - elif repanier_settings["TEST_MODE"]: + elif RepanierSettings.test_mode: coordinator = models.Staff.objects.filter(is_coordinator=True, is_active=True).order_by().first() if coordinator is not None: email.to = [coordinator.user.email] @@ -399,29 +399,29 @@ def recalculate_order_amount(permanence_id=None, if customer_id is None: if offer_item_queryset is not None: if permanence_status < PERMANENCE_SEND: - purchase_set = models.PurchaseOpenedForUpdate.objects\ + purchase_set = models.PurchaseOpenedOrClosedForUpdate.objects \ .filter(permanence_id=permanence_id, offer_item__in=offer_item_queryset)\ .order_by() else: - purchase_set = models.PurchaseClosedForUpdate.objects\ + purchase_set = models.PurchaseSendForUpdate.objects \ .filter(permanence_id=permanence_id, offer_item__in=offer_item_queryset)\ .order_by() else: if permanence_status < PERMANENCE_SEND: - purchase_set = models.PurchaseOpenedForUpdate.objects\ + purchase_set = models.PurchaseOpenedOrClosedForUpdate.objects \ .filter(permanence_id=permanence_id)\ .order_by() else: - purchase_set = models.PurchaseClosedForUpdate.objects\ + purchase_set = models.PurchaseSendForUpdate.objects \ .filter(permanence_id=permanence_id)\ .order_by() else: if permanence_status < PERMANENCE_SEND: - purchase_set = models.PurchaseOpenedForUpdate.objects\ + purchase_set = models.PurchaseOpenedOrClosedForUpdate.objects \ .filter(permanence_id=permanence_id, customer_id=customer_id)\ .order_by() else: - purchase_set = models.PurchaseClosedForUpdate.objects\ + purchase_set = models.PurchaseSendForUpdate.objects \ .filter(permanence_id=permanence_id, customer_id=customer_id)\ .order_by() @@ -492,7 +492,7 @@ def update_or_create_purchase(user_id=None, customer=None, offer_item_id=None, v # when the status is PERMANENCE_WAIT_FOR_SEND if (permanence.status == PERMANENCE_OPENED) or close_orders: # The offer_item belong to an open permanence - purchase = models.PurchaseOpenedForUpdate.objects.filter( + purchase = models.PurchaseOpenedOrClosedForUpdate.objects.filter( offer_item_id=offer_item.id, permanence_id=permanence.id, customer_id=customer.id)\ @@ -537,7 +537,7 @@ def update_or_create_purchase(user_id=None, customer=None, offer_item_id=None, v is_compensation = True else: is_compensation = False - models.PurchaseOpenedForUpdate.objects.create( + models.PurchaseOpenedOrClosedForUpdate.objects.create( permanence_id=permanence.id, permanence_date=permanence.permanence_date, offer_item_id=offer_item.id, @@ -566,7 +566,7 @@ def update_or_create_purchase(user_id=None, customer=None, offer_item_id=None, v def clean_offer_item(permanence, queryset, reorder=False): cur_language = translation.get_language() for offer_item in queryset: - offer_item.picture = offer_item.product.picture + offer_item.picture2 = offer_item.product.picture2 offer_item.reference = offer_item.product.reference offer_item.department_for_customer_id = offer_item.product.department_for_customer_id offer_item.producer_id = offer_item.product.producer_id @@ -602,10 +602,14 @@ def clean_offer_item(permanence, queryset, reorder=False): translation.activate(language["code"]) for offer_item in queryset: offer_item.long_name = offer_item.product.long_name - offer_item.cache_part_a = render_to_string('repanier/cache_part_a.html', {'offer': offer_item}) - offer_item.cache_part_b = render_to_string('repanier/cache_part_b.html', {'offer': offer_item}) - offer_item.cache_part_c = render_to_string('repanier/cache_part_c.html', {'offer': offer_item}) - offer_item.cache_part_e = render_to_string('repanier/cache_part_e.html', {'offer': offer_item}) + offer_item.cache_part_a = render_to_string('repanier/cache_part_a.html', + {'offer': offer_item, 'MEDIA_URL': settings.MEDIA_URL}) + offer_item.cache_part_b = render_to_string('repanier/cache_part_b.html', + {'offer': offer_item, 'MEDIA_URL': settings.MEDIA_URL}) + offer_item.cache_part_c = render_to_string('repanier/cache_part_c.html', + {'offer': offer_item, 'MEDIA_URL': settings.MEDIA_URL}) + offer_item.cache_part_e = render_to_string('repanier/cache_part_e.html', + {'offer': offer_item, 'MEDIA_URL': settings.MEDIA_URL}) offer_item.save_translations() if reorder: # The "order_by" of the queryset is only relevant after the previous "for" has been done. @@ -631,7 +635,7 @@ def clean_offer_item(permanence, queryset, reorder=False): .distinct("id", "tree_id", "lft") if departementforcustomer_set: pass - if repanier_settings['DISPLAY_PRODUCERS_ON_ORDER_FORM']: + if RepanierSettings.display_producer_on_order_form: producer_set = models.Producer.objects.filter(permanence=permanence.id).only("id", "short_profile_name") else: producer_set = None diff --git a/repanier/urls.py b/repanier/urls.py index 55d5eafa8602f1539ee61cdce654f3606ecf5e1b..8c8756e0d7bdde000a631cb4c664ae15f7ff563a 100644 --- a/repanier/urls.py +++ b/repanier/urls.py @@ -6,6 +6,7 @@ from views import OrderView, OrderViewWithoutCache, PreOrderView from views import CustomerInvoiceView from views import ProducerInvoiceView from views import PermanenceView +from picture.views import ajax_picture urlpatterns = patterns('', url(r'^go_repanier/$', views.login, name='login_form'), @@ -29,6 +30,7 @@ urlpatterns = patterns('', views.producer_product_description_ajax, name='producer_product_description_ajax'), url(r'^ajax/producer-product-description/(?P[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/(?P\d+)/$', views.producer_product_description_ajax, name='producer_product_description_ajax'), + url('^ajax/upload-picture/(?P.*)/(?P\d+)/$', ajax_picture, name='ajax_picture'), # url(r'^producer-product-description/(?P[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/(?P\d+)/$', # views.producer_product_description, name='producer_product_description'), diff --git a/repanier/views.py b/repanier/views.py index a1a61cd6928bd0e308f99686d72f6a188739587d..212123e1509238290c0d4fc8f5e0a2303c818dfc 100644 --- a/repanier/views.py +++ b/repanier/views.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import json import datetime +from os import sep as os_sep from django.contrib.sites.models import get_current_site from django.core import urlresolvers from django.core.mail import EmailMessage @@ -17,15 +18,15 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _not_lazy from django.views.decorators.csrf import csrf_protect from django.views.decorators.debug import sensitive_post_parameters +from django.views.decorators.http import require_GET from parler.models import TranslationDoesNotExist from django.utils import translation from django.db.models import Q from django.contrib.auth import (REDIRECT_FIELD_NAME, login as auth_login, logout as auth_logout, get_user_model) - +from apps import RepanierSettings from tools import * -from models import repanier_settings # from django.conf.urls import patterns, url from django.contrib.auth.decorators import login_required @@ -37,7 +38,7 @@ from django.views.generic import DetailView from django.shortcuts import render_to_response, get_object_or_404, resolve_url from django.template import RequestContext -from models import LUT_DepartmentForCustomer, PurchaseClosed, PurchaseOpened +from models import LUT_DepartmentForCustomer, PurchaseSend, PurchaseOpenedOrClosed from models import OfferItem from models import Permanence from models import Producer @@ -255,7 +256,8 @@ def me(request): customer.email2 = form.cleaned_data.get('email2').lower() customer.accept_mails_from_members = form.cleaned_data.get('accept_mails_from_members') customer.city = form.cleaned_data.get('city') - if repanier_settings['DELIVERY_POINT']: + customer.picture = form.cleaned_data.get('picture') + if RepanierSettings.delivery_point: customer.delivery_point = form.cleaned_data.get('delivery_point') customer.memo = form.cleaned_data.get('memo') customer.save() @@ -292,20 +294,24 @@ def me(request): field.initial = customer.accept_mails_from_members field = form.fields["city"] field.initial = customer.city - if repanier_settings['DELIVERY_POINT']: + field = form.fields["picture"] + field.initial = customer.picture + if hasattr(field.widget, 'upload_to'): + field.widget.upload_to = "customer" + os_sep + str(customer.id) + if RepanierSettings.delivery_point: field = form.fields["delivery_point"] field.initial = customer.delivery_point field = form.fields["memo"] field.initial = customer.memo - return render_response(request, "repanier/me_form.html", {'form': form, 'update': None}) +@require_GET def customer_product_description_ajax(request): # import sys # import traceback - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': offer_item_id = sint(request.GET.get('offer_item', 0)) offer_item = get_object_or_404(OfferItem, id=offer_item_id) if PERMANENCE_OPENED <= offer_item.permanence.status <= PERMANENCE_SEND: @@ -324,8 +330,9 @@ def customer_product_description_ajax(request): @never_cache +@require_GET def customer_name_ajax(request): - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': user = request.user if user.is_anonymous(): return HttpResponse(_('Anonymous')) @@ -337,8 +344,9 @@ def customer_name_ajax(request): @never_cache +@require_GET def my_balance_ajax(request): - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': user = request.user if user.is_anonymous(): return HttpResponse("") @@ -361,8 +369,9 @@ def my_balance_ajax(request): @never_cache +@require_GET def producer_name_ajax(request, offer_uuid=None): - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': producer = Producer.objects.filter(offer_uuid=offer_uuid, is_active=True).order_by().first() if producer is None: return HttpResponse(_('Anonymous')) @@ -372,7 +381,7 @@ def producer_name_ajax(request, offer_uuid=None): @never_cache def producer_product_description_ajax(request, offer_uuid=None, offer_item_id=None): - if offer_item_id is None or not repanier_settings['PRODUCER_PRE_OPENING']: + if offer_item_id is None or not RepanierSettings.producer_pre_opening: raise Http404 producer = Producer.objects.filter(offer_uuid=offer_uuid, is_active=True).order_by().first() if producer is None: @@ -406,6 +415,7 @@ def producer_product_description_ajax(request, offer_uuid=None, offer_item_id=No product.offer_description = form.cleaned_data.get('offer_description') product.customer_minimum_order_quantity = product.customer_increment_order_quantity product.customer_alert_order_quantity = product.stock + product.picture2 = form.cleaned_data.get('picture') product.save() offer_item_queryset = OfferItem.objects\ .filter( @@ -447,6 +457,9 @@ def producer_product_description_ajax(request, offer_uuid=None, offer_item_id=No field.initial = offer_item.vat_level field = form.fields["offer_description"] field.initial = offer_item.product.offer_description + field = form.fields["picture"] + field.initial = offer_item.product.picture2 + field.widget.upload_to = "product" + os_sep + str(offer_item.producer.id) update = None return render_response( @@ -458,9 +471,9 @@ def producer_product_description_ajax(request, offer_uuid=None, offer_item_id=No @never_cache +@require_GET def order_form_ajax(request): - result = "ko" - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': user = request.user if user.is_authenticated(): offer_item_id = sint(request.GET.get('offer_item', 0)) @@ -477,8 +490,9 @@ def order_form_ajax(request): @never_cache +@require_GET def order_init_ajax(request): - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': # construct a list which will contain all of the data for the response if 'offer_item' in request.GET: user = request.user @@ -525,13 +539,13 @@ def order_init_ajax(request): is_not_staff = Staff.objects.filter( customer_responsible_id=customer.id ).order_by().first() is None - if (repanier_settings['MAX_WEEK_WO_PARTICIPATION'] > DECIMAL_ZERO and is_not_staff) \ + if (RepanierSettings.max_week_wo_participation > DECIMAL_ZERO and is_not_staff) \ or len(permanence_boards) > 0: if len(permanence_boards) == 0: count_activity = PermanenceBoard.objects.filter( customer_id=customer.id, permanence_date__lt=now, permanence_date__gte=now - datetime.timedelta( - days=float(repanier_settings['MAX_WEEK_WO_PARTICIPATION'])*7 + days=float(RepanierSettings.max_week_wo_participation) * 7 ) ).count() else: @@ -568,7 +582,7 @@ def order_init_ajax(request): permanence = Permanence.objects.filter(id=offer_item.permanence_id)\ .only("status").order_by().first() if PERMANENCE_OPENED <= permanence.status <= PERMANENCE_SEND: - purchase = PurchaseOpened.objects.filter( + purchase = PurchaseOpenedOrClosed.objects.filter( offer_item_id=offer_item.id, customer_id=customer.id)\ .only("quantity_ordered").order_by().first() if purchase is not None: @@ -610,8 +624,10 @@ def order_init_ajax(request): @never_cache +@require_GET +# @login_required def order_select_ajax(request): - if request.is_ajax() and request.method == 'GET': + if request.is_ajax(): # and request.method == 'GET': # construct a list which will contain all of the data for the response user = request.user to_json = [] @@ -629,7 +645,7 @@ def order_select_ajax(request): permanence = Permanence.objects.filter(id=offer_item.permanence_id)\ .only("status").order_by().first() if PERMANENCE_OPENED <= permanence.status <= PERMANENCE_SEND: - purchase = PurchaseOpened.objects.filter( + purchase = PurchaseOpenedOrClosed.objects.filter( offer_item_id=offer_item.id, customer_id=customer.id)\ .only("quantity_ordered").order_by().first() if purchase is not None: @@ -784,7 +800,7 @@ class OrderView(ListView): context['permanence'] = self.permanence if self.permanence is not None and self.permanence.status == PERMANENCE_OPENED: context['display_all_product_button'] = "Ok" - if repanier_settings['DISPLAY_PRODUCERS_ON_ORDER_FORM']: + if RepanierSettings.display_producer_on_order_form: producer_set = Producer.objects.filter(permanence=self.permanence.id).only("id", "short_profile_name") else: producer_set = None @@ -884,7 +900,7 @@ class OrderViewWithoutCache(OrderView): return context def get_queryset(self): - if not repanier_settings['DISPLAY_ANONYMOUS_ORDER_FORM']: + if not RepanierSettings.display_anonymous_order_form: return OfferItem.objects.none() else: if not self.user.is_authenticated() and self.offeritem_id is not None and self.offeritem_id == 'purchased': @@ -984,16 +1000,16 @@ class CustomerInvoiceView(DetailView): if customer_invoice: bank_account_set = BankAccount.objects.filter(customer_invoice=customer_invoice) context['bank_account_set'] = bank_account_set - context['DISPLAY_VAT'] = repanier_settings['DISPLAY_VAT'] - if repanier_settings['DISPLAY_PRODUCERS_ON_ORDER_FORM']: + context['DISPLAY_VAT'] = RepanierSettings.display_vat + if RepanierSettings.display_producer_on_order_form: context['DISPLAY_PRODUCERS_ON_ORDER_FORM'] = True - purchase_set = PurchaseClosed.objects.filter( + purchase_set = PurchaseSend.objects.filter( Q(customer_invoice=customer_invoice, quantity_invoiced__gt=DECIMAL_ZERO, offer_item__translations__language_code=translation.get_language()) | Q(customer_invoice=customer_invoice, comment__gt="", offer_item__translations__language_code=translation.get_language()) ).order_by("producer", "offer_item__translations__order_sort_order") else: context['DISPLAY_PRODUCERS_ON_ORDER_FORM'] = False - purchase_set = PurchaseClosed.objects.filter( + purchase_set = PurchaseSend.objects.filter( Q(customer_invoice=customer_invoice, quantity_invoiced__gt=DECIMAL_ZERO, offer_item__translations__language_code=translation.get_language()) | Q(customer_invoice=customer_invoice, comment__gt="", offer_item__translations__language_code=translation.get_language()) ).order_by("offer_item__translations__order_sort_order") @@ -1152,7 +1168,7 @@ class PreOrderView(DetailView): ), permanence_id=permanence_pre_opened.id, translations__language_code=translation.get_language(), - producer_price_are_wo_vat=True, + # producer_price_are_wo_vat=True, is_active=True ).order_by( "translations__long_name" diff --git a/repanier/widget.py b/repanier/widget.py index eec6f133ba7ec4d63048a6f684ff20cff7eff59c..a0b05a738d7528f1739ebdd1bb255868db53b4b4 100644 --- a/repanier/widget.py +++ b/repanier/widget.py @@ -242,73 +242,74 @@ class SelectAdminOrderUnitWidget(forms.Select): output.append('') return mark_safe('\n'.join(output)) diff --git a/repanier/xslx/xslx_invoice.py b/repanier/xslx/xslx_invoice.py index 43c98a0c5b84e176960196f06c1b6542e4cf09ee..e565b9546a53a7ad8679bc93f6e9e3da16a6fdbc 100644 --- a/repanier/xslx/xslx_invoice.py +++ b/repanier/xslx/xslx_invoice.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 from __future__ import unicode_literals -from repanier.models import repanier_settings +from repanier.apps import RepanierSettings from django.utils import translation from django.contrib.sites.models import get_current_site from django.http import HttpResponse @@ -15,7 +15,7 @@ from repanier.models import Customer from repanier.models import CustomerInvoice from repanier.models import Producer from repanier.models import ProducerInvoice -from repanier.models import PurchaseClosed +from repanier.models import PurchaseSend from repanier.tools import get_invoice_unit from repanier.xslx.xslx_stock import export_stock @@ -171,7 +171,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): ws = None - purchase_set = PurchaseClosed.objects.filter( + purchase_set = PurchaseSend.objects.filter( permanence_id=permanence.id, offer_item__translations__language_code=translation.get_language() ).order_by( @@ -181,7 +181,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): hide_producer_prices = False hide_customer_prices = False elif customer is not None: - purchase_set = PurchaseClosed.objects.filter( + purchase_set = PurchaseSend.objects.filter( permanence_id=permanence.id, customer=customer, offer_item__translations__language_code=translation.get_language() ).order_by( @@ -190,7 +190,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): hide_producer_prices = True hide_customer_prices = False else: - purchase_set = PurchaseClosed.objects.filter( + purchase_set = PurchaseSend.objects.filter( permanence_id=permanence.id, producer=producer, offer_item__translations__language_code=translation.get_language() ).order_by( @@ -235,7 +235,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): (_("Producer"), 15, purchase.producer.short_profile_name, NumberFormat.FORMAT_TEXT, False), (_("Basket"), 20, purchase.customer.short_basket_name, NumberFormat.FORMAT_TEXT, False), (_("Department"), 15, - purchase.offer_item.department_for_customer.short_name, + purchase.offer_item.department_for_customer.short_name if purchase.offer_item.department_for_customer is not None else "", NumberFormat.FORMAT_TEXT, False), (_("Product"), 60, purchase.offer_item.get_long_name(), NumberFormat.FORMAT_TEXT, False), (_("Quantity"), 10, qty, '#,##0.????', @@ -272,7 +272,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): (_("selling price"), 10, purchase.selling_price, '_ € * #,##0.00_ ;_ € * -#,##0.00_ ;_ € * "-"??_ ;_ @_ ', False), ] - if repanier_settings['DISPLAY_VAT']: + if RepanierSettings.display_vat: row += [ (_("Vat"), 10, DECIMAL_ZERO if purchase.invoiced_price_with_compensation else (purchase.offer_item.customer_vat * purchase.get_quantity()).quantize(FOUR_DECIMALS), @@ -282,7 +282,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): row += [ (_("Vat"), 10, '', NumberFormat.FORMAT_TEXT, False), ] - if repanier_settings['DISPLAY_VAT'] or not hide_producer_prices: + if RepanierSettings.display_vat or not hide_producer_prices: row += [ (_("Compensation"), 10, (purchase.offer_item.compensation * purchase.get_quantity()).quantize(FOUR_DECIMALS) if purchase.invoiced_price_with_compensation else DECIMAL_ZERO, @@ -325,7 +325,7 @@ def export(permanence, customer=None, producer=None, wb=None, sheet_name=""): ws.column_dimensions[get_column_letter(8)].visible = False ws.column_dimensions[get_column_letter(9)].visible = False ws.column_dimensions[get_column_letter(10)].visible = False - if not repanier_settings['DISPLAY_VAT']: + if not RepanierSettings.display_vat: ws.column_dimensions[get_column_letter(13)].visible = False if hide_customer_prices: ws.column_dimensions[get_column_letter(11)].visible = False diff --git a/repanier/xslx/xslx_offer.py b/repanier/xslx/xslx_offer.py index 1054872eb3f65e43a6626bc87ccc36da4a1902f1..32b573c2f43c09eb80d562ae92ad41e04625d3a1 100644 --- a/repanier/xslx/xslx_offer.py +++ b/repanier/xslx/xslx_offer.py @@ -39,7 +39,9 @@ def export(permanence, wb=None): "order_average_weight"): row = [ (_("Producer"), 15, product.producer.short_profile_name, NumberFormat.FORMAT_TEXT, False), - (_("Department"), 15, product.department_for_customer.short_name, NumberFormat.FORMAT_TEXT, + (_("Department"), 15, + product.department_for_customer.short_name if product.department_for_customer is not None else "", + NumberFormat.FORMAT_TEXT, False), (_("Product"), 60, product.get_long_name(), NumberFormat.FORMAT_TEXT, False), (_("Unit Price"), 10, product.producer_unit_price, @@ -111,7 +113,8 @@ def export(permanence, wb=None): row = [ (_("Producer"), 15, offer_item.producer.short_profile_name, NumberFormat.FORMAT_TEXT, False), - (_("Department"), 15, offer_item.department_for_customer.short_name, + (_("Department"), 15, + offer_item.department_for_customer.short_name if offer_item.department_for_customer is not None else "", NumberFormat.FORMAT_TEXT, False), (_("Product"), 60, offer_item.get_long_name(), NumberFormat.FORMAT_TEXT, False), (_("Unit Price"), 10, offer_item.producer_unit_price, diff --git a/repanier/xslx/xslx_order.py b/repanier/xslx/xslx_order.py index 82e44e159b72ab71ed8c16425c18117227ea1c8f..ec3351cd17ea0689330b7b34d3fcf42094a274d3 100644 --- a/repanier/xslx/xslx_order.py +++ b/repanier/xslx/xslx_order.py @@ -10,11 +10,11 @@ from openpyxl.style import NumberFormat from openpyxl.styles import Color from openpyxl.workbook import Workbook from django.contrib.sites.models import Site -from repanier.apps import repanier_settings +from repanier.apps import RepanierSettings from export_tools import * from repanier.const import * -from repanier.models import Customer, PurchaseOpened, OfferItem +from repanier.models import Customer, PurchaseOpenedOrClosed, OfferItem from repanier.models import Permanence from repanier.models import PermanenceBoard from repanier.models import Producer @@ -198,7 +198,7 @@ def export_preparation(permanence, wb=None): while producer is not None: producer_save = producer if producer.invoice_by_basket: - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, producer_id=producer.id, offer_item__translations__language_code=translation.get_language() @@ -322,7 +322,7 @@ def export_preparation(permanence, wb=None): else: # Using quantity_for_preparation_sort_order the order is by customer__short_basket_name if the product # is to be distributed by piece, otherwise by lower qty first. - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, producer_id=producer.id, offer_item__translations__language_code=translation.get_language() @@ -497,7 +497,7 @@ def export_producer_by_product(permanence, producer, wb=None): if offer_item.wrapped: # or offer_item.limit_order_quantity_to_stock: hide_column_short_basket_name = False first_purchase = True - for purchase in PurchaseOpened.objects.filter( + for purchase in PurchaseOpenedOrClosed.objects.filter( offer_item_id=offer_item.id, offer_item__translations__language_code=translation.get_language() ).exclude( @@ -572,7 +572,7 @@ def export_producer_by_product(permanence, producer, wb=None): qty = (qty / offer_item.order_average_weight).quantize(TWO_DECIMALS) stock = DECIMAL_ZERO c = ws.cell(row=row_num, column=0) - c.value = repanier_settings['GROUP_NAME'] + c.value = RepanierSettings.group_name c.style.number_format.format_code = NumberFormat.FORMAT_TEXT c.style.borders.bottom.border_style = Border.BORDER_THIN c = ws.cell(row=row_num, column=1) @@ -648,7 +648,7 @@ def export_producer_by_product(permanence, producer, wb=None): c = ws.cell(row=row_num, column=col_num) c.style.borders.bottom.border_style = Border.BORDER_THIN if col_num == 1: - c.value = "%s %s" % (_("Total Price"), repanier_settings['GROUP_NAME']) + c.value = "%s %s" % (_("Total Price"), RepanierSettings.group_name) c.style.number_format.format_code = NumberFormat.FORMAT_TEXT if col_num == 6: c.value = "=" + "+".join(formula_main_total) @@ -684,7 +684,7 @@ def export_producer_by_customer(permanence, producer, wb=None): show_column_reference = False hide_column_unit_deposit = True formula_main_total = [] - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, producer_id=producer.id, offer_item__translations__language_code=translation.get_language() @@ -761,7 +761,7 @@ def export_producer_by_customer(permanence, producer, wb=None): c = ws.cell(row=row_num, column=col_num) c.style.borders.bottom.border_style = Border.BORDER_THIN if col_num == 0: - c.value = "%s %s" % (_("Total Price"), repanier_settings['GROUP_NAME']) + c.value = "%s %s" % (_("Total Price"), RepanierSettings.group_name) c.style.number_format.format_code = NumberFormat.FORMAT_TEXT if col_num == 5: c.value = "=" + "+".join(formula_main_total) @@ -796,7 +796,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non if customer is not None: translation.activate(customer.language) if deposit: - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, customer_id=customer.id, producer__isnull=False, @@ -806,7 +806,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non "offer_item__translations__order_sort_order" ).iterator() else: - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, customer_id=customer.id, producer__isnull=False, @@ -819,7 +819,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non ).iterator() else: if deposit: - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, producer__isnull=False, offer_item__translations__language_code=translation.get_language(), @@ -829,7 +829,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non "offer_item__translations__order_sort_order" ).iterator() else: - purchases = PurchaseOpened.objects.filter( + purchases = PurchaseOpenedOrClosed.objects.filter( permanence_id=permanence.id, producer__isnull=False, offer_item__translations__language_code=translation.get_language(), @@ -843,7 +843,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non purchase = next_purchase(purchases) if purchase: if deposit: - if repanier_settings['PAGE_BREAK_ON_CUSTOMER_CHECK']: + if RepanierSettings.page_break_on_customer_check: # Change the orientation to reduce the number of page breaks, i.e. the number of printed pages wb, ws = new_landscape_a4_sheet( wb, @@ -859,7 +859,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non header ) else: - if repanier_settings['PAGE_BREAK_ON_CUSTOMER_CHECK']: + if RepanierSettings.page_break_on_customer_check: # Change the orientation to reduce the number of page breaks, i.e. the number of printed pages wb, ws = new_landscape_a4_sheet( wb, @@ -984,7 +984,7 @@ def export_customer(permanence, customer=None, wb=None, ws_preparation_title=Non c = ws.cell(row=row_num, column=col_num) c.style.borders.bottom.border_style = Border.BORDER_MEDIUMDASHED row_num += 2 - if deposit and repanier_settings['PAGE_BREAK_ON_CUSTOMER_CHECK']: + if deposit and RepanierSettings.page_break_on_customer_check: ws.page_breaks.append(row_num) ws.column_dimensions[get_column_letter(1)].visible = False diff --git a/repanier/xslx/xslx_product.py b/repanier/xslx/xslx_product.py index bed445e7292f8d5d7e151954b9b952027ce67a08..982bf64342d9dae4fb48beda904cfde57b41bafb 100644 --- a/repanier/xslx/xslx_product.py +++ b/repanier/xslx/xslx_product.py @@ -23,7 +23,7 @@ from repanier.tools import cap from views import import_xslx_view -def export(producer, wb=None): +def export(producer, wb=None, producer_prices=True): if wb is None: wb = Workbook() ws = wb.get_active_sheet() @@ -34,22 +34,23 @@ def export(producer, wb=None): product_set = Product.objects.filter( producer_id=producer.id, is_active=True ) - product_save = None now = timezone.localtime(timezone.now()) worksheet_setup_landscape_a4(ws, producer.short_profile_name, now.strftime('%d-%m-%Y %H:%M')) for product in product_set: row = [ (_("Id"), 10, product.id, '#,##0', False), - (_("department_for_customer"), 15, product.department_for_customer.short_name, + (_("department_for_customer"), 15, + product.department_for_customer.short_name if product.department_for_customer is not None else " ", NumberFormat.FORMAT_TEXT, False), - (_("is_into_offer"), 7, _("Yes") if product.is_into_offer else None, + (_("is_into_offer"), 7, _("Yes") if product.is_into_offer else _("No"), NumberFormat.FORMAT_TEXT, False), (_("long_name"), 60, product.long_name, NumberFormat.FORMAT_TEXT, False), (_("order unit"), 15, product.get_order_unit_display(), NumberFormat.FORMAT_TEXT, False), - (_("wrapped"), 7, _("Yes") if product.wrapped else None, + (_("wrapped"), 7, _("Yes") if product.wrapped else _("No"), NumberFormat.FORMAT_TEXT, False), (_("order_average_weight"), 10, product.order_average_weight, '#,##0.???', False), - (_("producer unit price"), 10, product.producer_unit_price, + (_("producer unit price"), 10, + product.producer_unit_price if producer_prices else product.customer_unit_price, '_ € * #,##0.00_ ;_ € * -#,##0.00_ ;_ € * "-"??_ ;_ @_ ', False), (_("deposit"), 10, product.unit_deposit, '_ € * #,##0.00_ ;_ € * -#,##0.00_ ;_ € * "-"??_ ;_ @_ ', False), @@ -80,10 +81,6 @@ def export(producer, wb=None): c.style.borders.right.border_style = Border.BORDER_THIN else: c.style.borders.bottom.border_style = Border.BORDER_HAIR - if product_save != product.department_for_customer.id: - c.style.borders.top.border_style = Border.BORDER_THIN - if product_save != product.department_for_customer.id: - product_save = product.department_for_customer.id row_num += 1 # Now, for helping the user encoding new purchases row = [ @@ -135,13 +132,13 @@ def export(producer, wb=None): dv.ranges.append('E2:E5000') # Data validation Yes/ # List of Yes/ - valid_values = [_('Yes'), ] + valid_values = [_('Yes'), _("No")] dv = DataValidation(ValidationType.LIST, formula1=get_validation_formula(wb=wb, valid_values=valid_values), allow_blank=True) ws.add_data_validation(dv) dv.ranges.append('C2:C5000') # List of Yes/ - valid_values = [_('Yes'), ] + valid_values = [_('Yes'), _("No")] dv = DataValidation(ValidationType.LIST, formula1=get_validation_formula(wb=wb, valid_values=valid_values), allow_blank=True) ws.add_data_validation(dv) @@ -180,14 +177,14 @@ def export(producer, wb=None): return wb -def admin_export(request, queryset): +def admin_export(request, queryset, producer_prices=True): queryset = queryset.order_by("-short_profile_name") wb = None response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') filename = ("%s.xlsx" % (_("products"))).encode('ascii', errors='replace').replace('?', '_') response['Content-Disposition'] = 'attachment; filename=' + filename for producer in queryset: - wb = export(producer=producer, wb=wb) + wb = export(producer=producer, wb=wb, producer_prices=producer_prices) wb.save(response) return response @@ -212,10 +209,7 @@ def import_product_sheet(worksheet, producer=None, if row[_('department_for_customer')] in department_for_customer_2_id_dict: department_for_customer_id = department_for_customer_2_id_dict[row[_('department_for_customer')]] else: - error = True - # transaction.rollback() - error_msg = _("Row %(row_num)d : No valid departement for customer") % {'row_num': row_num + 1} - break + department_for_customer_id = None producer_unit_price = None if row[_('producer_unit_price')] is None else Decimal( row[_('producer_unit_price')]) @@ -261,7 +255,10 @@ def import_product_sheet(worksheet, producer=None, # Let only update if the given id is the same as the product found id product.producer_id = producer.id product.long_name = long_name - product.department_for_customer_id = department_for_customer_id + if department_for_customer_id is not None: + product.department_for_customer_id = department_for_customer_id + else: + product.department_for_customer = None product.order_unit = order_unit if order_average_weight is not None: product.order_average_weight = order_average_weight @@ -275,8 +272,8 @@ def import_product_sheet(worksheet, producer=None, product.customer_increment_order_quantity = customer_increment_order_quantity if customer_alert_order_quantity is not None: product.customer_alert_order_quantity = customer_alert_order_quantity - product.is_into_offer = (row[_('is_into_offer')] is not None) - product.wrapped = (row[_('wrapped')] is not None) + product.is_into_offer = (row[_('is_into_offer')] == _("Yes")) + product.wrapped = (row[_('wrapped')] == _("Yes")) product.vat_level = vat_level product.is_active = True # product.product_reorder = product_reorder diff --git a/repanier/xslx/xslx_purchase.py b/repanier/xslx/xslx_purchase.py index 03027aca8be921ba2025f58c3d797f8a1ffef462..56c10e37c2a62d4a26f26c25ebdac2cba9ef0c0c 100644 --- a/repanier/xslx/xslx_purchase.py +++ b/repanier/xslx/xslx_purchase.py @@ -16,7 +16,7 @@ from django.contrib.sites.models import Site from export_tools import * from import_tools import * from repanier.const import * -from repanier.models import PurchaseClosed, PurchaseClosedForUpdate +from repanier.models import PurchaseSend, PurchaseSendForUpdate from repanier.models import Producer from repanier.tools import cap from views import import_xslx_view @@ -74,7 +74,7 @@ def export_purchase(permanence=None, year=None, queryset=None, wb=None): count_producer_purchase = 0 if producer.invoice_by_basket: if year is None: - purchases = PurchaseClosed.objects.filter( + purchases = PurchaseSend.objects.filter( permanence_id=permanence.id, producer_id=producer.id, offer_item__translations__language_code=translation.get_language() @@ -83,7 +83,7 @@ def export_purchase(permanence=None, year=None, queryset=None, wb=None): "offer_item__translations__order_sort_order" ).iterator() else: - purchases = PurchaseClosed.objects.filter( + purchases = PurchaseSend.objects.filter( permanence__status__gte=PERMANENCE_DONE, permanence__permanence_date__year=year, producer_id=producer.id, @@ -229,7 +229,7 @@ def export_purchase(permanence=None, year=None, queryset=None, wb=None): if year is None: # Using quantity_for_preparation_sort_order the order is by customer__short_basket_name if the product # is to be distributed by piece, otherwise by lower qty first. - purchases = PurchaseClosed.objects.filter( + purchases = PurchaseSend.objects.filter( permanence_id=permanence.id, producer_id=producer.id, offer_item__translations__language_code=translation.get_language() @@ -239,7 +239,7 @@ def export_purchase(permanence=None, year=None, queryset=None, wb=None): "customer__short_basket_name" ).iterator() else: - purchases = PurchaseClosed.objects.filter( + purchases = PurchaseSend.objects.filter( permanence__status__gte=PERMANENCE_DONE, permanence__permanence_date__year=year, producer_id=producer.id, @@ -462,7 +462,7 @@ def import_purchase_sheet(worksheet, permanence=None, break row_id = Decimal(row[_('Id')]) - purchase = PurchaseClosedForUpdate.objects.filter(id=row_id).order_by().first() + purchase = PurchaseSendForUpdate.objects.filter(id=row_id).order_by().first() if purchase is None: error = True error_msg = _("Row %(row_num)d : No purchase corresponding to the given purchase id.") % { diff --git a/repanier/xslx/xslx_stock.py b/repanier/xslx/xslx_stock.py index 9142cf504423a3b82b01f5cb78ab8f435b636ba9..a7e908d0e67a5a2e2335cdc24479c261d7958129 100644 --- a/repanier/xslx/xslx_stock.py +++ b/repanier/xslx/xslx_stock.py @@ -78,12 +78,12 @@ def export_stock(permanence, customer_price=False, wb=None, ws_customer_title=No while offer_item is not None and producer_save.id == offer_item.producer_id: department_for_customer_save = offer_item.department_for_customer c = ws.cell(row=row_num, column=3) - c.value = producer_save.short_profile_name + " - " + department_for_customer_save.short_name + c.value = producer_save.short_profile_name + " - " + department_for_customer_save.short_name if department_for_customer is not None else "" c.style.borders.bottom.border_style = Border.BORDER_THIN c.style.alignment.horizontal = 'right' c.style.font.italic = True row_num += 1 - while offer_item is not None and producer_save.id == offer_item.producer_id and department_for_customer_save.id == offer_item.department_for_customer_id: + while offer_item is not None and producer_save.id == offer_item.producer_id and department_for_customer_save == offer_item.department_for_customer: if len(offer_item.reference) < 36: offer_item_reference = offer_item.reference show_column_reference = True