Commit 91684ae1 authored by Patrick's avatar Patrick

release used in production

parent 8db31f0d
No preview for this file type
...@@ -83,14 +83,14 @@ Install Required libs for Django installation ...@@ -83,14 +83,14 @@ Install Required libs for Django installation
.. code:: bash .. code:: bash
sudo apt-get install gettext unzip sudo apt-get install gettext unzip
sudo apt-get python-setuptools sudo apt-get install python-setuptools
# vv For the PostgreSQL connector # vv For the PostgreSQL connector
sudo apt-get install libpq-dev python-dev sudo apt-get install libpq-dev python-dev
# vv for Pillow # vv for Pillow
sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev
# vv for docx # vv for docx
sudo apt-get install libxml2-dev libxslt1-dev sudo apt-get install libxml2-dev libxslt1-dev
......
...@@ -57,13 +57,13 @@ Check if CMS install is ok ...@@ -57,13 +57,13 @@ Check if CMS install is ok
python manage.py cms check --settings=ptidej.ptidej_settings python manage.py cms check --settings=ptidej.ptidej_settings
Compile translation files .. Compile translation files
.. code:: bash .. .. code:: bash
cd ~/v1/mysite/repanier .. cd ~/v1/mysite/repanier
export DJANGO_SETTINGS_MODULE= .. export DJANGO_SETTINGS_MODULE=
django-admin.py compilemessages .. django-admin.py compilemessages
If you want, initialize the DB with test content : copy the content of /install/createdb into ~/v1/mysite/ If you want, initialize the DB with test content : copy the content of /install/createdb into ~/v1/mysite/
...@@ -95,6 +95,10 @@ Restart Nginx and Uwsgi ...@@ -95,6 +95,10 @@ Restart Nginx and Uwsgi
# Start Uwsgi # Start Uwsgi
sudo /etc/init.d/uwsgi start sudo /etc/init.d/uwsgi start
# The same Stop, Clean, Start Uwsgi in one line
sudo /etc/init.d/uwsgi stop && rm -rf /var/tmp/django_cache/* && sudo /etc/init.d/uwsgi start
The surf on your sites The surf on your sites
When needed, upgrade the DB with south for a new version of specific INSTALLED_APPS (eg repanier) When needed, upgrade the DB with south for a new version of specific INSTALLED_APPS (eg repanier)
......
...@@ -16,6 +16,7 @@ pip install psycopg2 ...@@ -16,6 +16,7 @@ pip install psycopg2
# ELSE installing the latest development version # ELSE installing the latest development version
# pip install https://github.com/divio/django-cms/archive/develop.zip # pip install https://github.com/divio/django-cms/archive/develop.zip
# OR UPGRADE # OR UPGRADE
pip install -U django
pip install -U https://github.com/divio/django-cms/archive/develop.zip pip install -U https://github.com/divio/django-cms/archive/develop.zip
# ELSE installing a specific version # ELSE installing a specific version
# pip install https://github.com/divio/django-cms/archive/3.0.0.beta3.zip # pip install https://github.com/divio/django-cms/archive/3.0.0.beta3.zip
...@@ -37,10 +38,12 @@ pip install -U django_compressor ...@@ -37,10 +38,12 @@ pip install -U django_compressor
pip install -U django-admin-sortable2 pip install -U django-admin-sortable2
pip install -U openpyxl pip install -U openpyxl
# pip install -U django-hvad # pip install -U django-hvad
pip install -U docx # pip install -U docx
# pip install django_debug_toolbar # pip install django_debug_toolbar
# pip install django-dajaxice ! not working with Django 1.6 # pip install django-dajaxice ! not working with Django 1.6
# pip install django-custom-user # pip install django-custom-user
# pip install django-registration # pip install django-registration
pip install -U django-crispy-forms pip install -U django-crispy-forms
pip install -U django-celery # pip install -U django-celery
# pip install -U celery
pip install -U django-password-reset
\ No newline at end of file
...@@ -19,7 +19,13 @@ ADMINS = ( ...@@ -19,7 +19,13 @@ ADMINS = (
os.getenv('DJANGO_SETTINGS_MODULE_ADMIN_EMAIL','') os.getenv('DJANGO_SETTINGS_MODULE_ADMIN_EMAIL','')
), ),
) )
SERVER_EMAIL = os.getenv('DJANGO_SETTINGS_MODULE_ADMIN_EMAIL','')
# MANAGERS = (
# (
# os.getenv('DJANGO_SETTINGS_MODULE_ADMIN_NAME',''),
# os.getenv('DJANGO_SETTINGS_MODULE_ADMIN_EMAIL','')
# ),
# )
###################### ######################
DATABASES = { DATABASES = {
...@@ -208,6 +214,11 @@ LOGOUT_URL = "/leave_repanier/" ...@@ -208,6 +214,11 @@ LOGOUT_URL = "/leave_repanier/"
# ], # ],
# } # }
CKEDITOR_SETTINGS = {
'language': '{{ language }}',
'toolbar': 'HTMLField',
}
TEXT_SAVE_IMAGE_FUNCTION='cmsplugin_filer_image.integrations.ckeditor.create_image_plugin' TEXT_SAVE_IMAGE_FUNCTION='cmsplugin_filer_image.integrations.ckeditor.create_image_plugin'
FILER_ENABLE_LOGGING = False FILER_ENABLE_LOGGING = False
......
...@@ -8,8 +8,13 @@ ...@@ -8,8 +8,13 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
{% addtoblock "css" %}<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">{% endaddtoblock %} {% addtoblock "css" %}<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">{% endaddtoblock %}
{% addtoblock "css" %}<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">{% endaddtoblock %}
{% addtoblock "css" %}<link rel="stylesheet" href="{{ STATIC_URL}}bootstrap/css/custom.css">{% endaddtoblock %} {% addtoblock "css" %}<link rel="stylesheet" href="{{ STATIC_URL}}bootstrap/css/custom.css">{% endaddtoblock %}
{% addtoblock "css" %} {% addtoblock "css" %}
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<style type="text/css"> <style type="text/css">
a.skip_link{ a.skip_link{
position:absolute; position:absolute;
...@@ -52,23 +57,31 @@ ...@@ -52,23 +57,31 @@
<li><a href="{% url "logout_form" %}">{% trans "Logout" %}</a></li> <li><a href="{% url "logout_form" %}">{% trans "Logout" %}</a></li>
</ul> </ul>
</li> </li>
<li class="dropdown">
<a href="?offeritem=purchased"><span id="my_basket">{{ prepared_amount }}</span> &euro; <span class="glyphicon glyphicon-shopping-cart"></span></a>
</li>
{% else %} {% else %}
<li class="dropdown"> <li class="dropdown">
<a href="{% url "login_form" %}">{% trans "Login" %}</a> <a href="{% url "login_form" %}">{% trans "Login" %}</a>
</li> </li>
{% endif %} {% endif %}
{% if languages.len > 1 %}
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% trans "Language" %} <b class="caret"></b></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown">{% trans "Language" %} <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% language_chooser "native" %} {% language_chooser "native" %}
</ul> </ul>
</li> </li>
{% endif %}
</ul> </ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>
</div> </div>
<!-- Begin page content --> <!-- Begin page content -->
<a name="content"></a> <a name="content"></a>
<!--[if lt IE 9]>
<h1>{% trans "It will be easier for you to crawl this website with a newer browser. For example" %} <a href="http://www.mozilla.org/fr/">Firefox</a> {% trans "or" %} <a href="http://www.google.be/intl/fr/chrome/browser/">Chrome</a>.</h1>
<![endif]-->
{% block base_content%}{% endblock %} {% block base_content%}{% endblock %}
<!-- Footer --> <!-- Footer -->
<div id="footer" class="container"> <div id="footer" class="container">
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
<p>Repanier est un outil de gestion du cycle des permamnences destiné aux groupements d'achats et aux producteurs locaux.</p> <p>Repanier est un outil de gestion du cycle des permamnences destiné aux groupements d'achats et aux producteurs locaux.</p>
<p>Contraction de repas et panier, repanier est le fruit de l'expérience des membres de plusieurs groupements d'achats</p> <p>Contraction de repas et panier, repanier est le fruit de l'expérience des membres de plusieurs groupements d'achats</p>
<ul> <ul>
<li><a href="http://ptidej.gasath.be/">Gasath, groupe ptidej</a>, une trentaine d'acheteurs de produits locaux, de préférence bio, sur Ath</li> <li><a href="http://ptidej.repanier.be/">Gasath, groupe ptidej</a>, une trentaine d'acheteurs de produits locaux, de préférence bio, sur Ath</li>
<li><a href="http://apero.gasath.be/">Gasath, groupe apéro</a>, une trentaine d'acheteurs de produits locaux et bio sur Ath</li> <li><a href="http://apero.repanier.be/">Gasath, groupe apéro</a>, une trentaine d'acheteurs de produits locaux et bio sur Ath</li>
<li><a href="http://www.lepanierlensois.be/">Le Panier Lensois</a>, une trentaine d'acheteurs de produits locaux et bio sur Lens</li> <li><a href="http://www.lepanierlensois.be/">Le Panier Lensois</a>, une trentaine d'acheteurs de produits locaux et bio sur Lens</li>
<li><a href="http://www.lebiodici.be/">Le Bio d'Ici</a>, une quinzaine de maraîchers bio regroupés pour les restaurants, collectivités locales, ...</li> <li><a href="http://www.lebiodici.be/">Le Bio d'Ici</a>, une quinzaine de maraîchers bio regroupés pour les restaurants, collectivités locales, ...</li>
</ul> </ul>
......
{% load menu_tags i18n %} {% load menu_tags i18n %}
{% for language in languages %} {% for language in languages %}
<li><a href="{% page_language_url language.0 %}" {% ifequal current_language language.0 %} class="active"{% endifequal %}>{{ language.1 }}</a></li> <li><a href="{% page_language_url language.0 %}" {% ifequal current_language language.0 %} class="active"{% endifequal %}>{{ language.1 }}</a></li>
{% if not forloop.last %}
<!-- <li role="presentation" class="divider"></li> -->
{% endif %}
{% endfor %} {% endfor %}
\ No newline at end of file
...@@ -17,6 +17,7 @@ from django.contrib.auth import get_user_model ...@@ -17,6 +17,7 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core import urlresolvers from django.core import urlresolvers
import datetime import datetime
from django.utils import timezone
from django.utils.timezone import utc from django.utils.timezone import utc
from django import forms from django import forms
...@@ -32,7 +33,7 @@ from django.contrib.sites.models import get_current_site ...@@ -32,7 +33,7 @@ from django.contrib.sites.models import get_current_site
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
# from adminsortable.admin import SortableAdminMixin # from adminsortable.admin import SortableAdminMixin
from repanier.adminsortable import SortableAdminMixin # from repanier.adminsortable import SortableAdminMixin
from repanier.models import LUT_ProductionMode from repanier.models import LUT_ProductionMode
from repanier.models import LUT_DepartmentForCustomer from repanier.models import LUT_DepartmentForCustomer
...@@ -57,6 +58,7 @@ from repanier.views import render_response ...@@ -57,6 +58,7 @@ from repanier.views import render_response
from repanier.admin_export_xlsx import export_permanence_planified_xlsx from repanier.admin_export_xlsx import export_permanence_planified_xlsx
from repanier.admin_export_xlsx import export_orders_xlsx from repanier.admin_export_xlsx import export_orders_xlsx
from repanier.admin_export_xlsx import export_product_xlsx from repanier.admin_export_xlsx import export_product_xlsx
from repanier.admin_export_xlsx import export_invoices_xlsx
from repanier.admin_export_xlsx import export_permanence_done_xlsx from repanier.admin_export_xlsx import export_permanence_done_xlsx
from repanier.admin_import_xlsx import import_product_xlsx from repanier.admin_import_xlsx import import_product_xlsx
from repanier.admin_import_xlsx import import_permanence_done_xlsx from repanier.admin_import_xlsx import import_permanence_done_xlsx
...@@ -66,10 +68,7 @@ from repanier.admin_send_mail import send_alert_email ...@@ -66,10 +68,7 @@ from repanier.admin_send_mail import send_alert_email
from menus.base import Menu, NavigationNode from menus.base import Menu, NavigationNode
from menus.menu_pool import menu_pool from menus.menu_pool import menu_pool
from repanier.tasks import open_offers_async from repanier import tasks
from repanier.tasks import close_orders_async
from repanier.tasks import done_async
from repanier.tasks import email_invoices_async
# Filters in the right sidebar of the change list page of the admin # Filters in the right sidebar of the change list page of the admin
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
...@@ -197,21 +196,24 @@ class PurchaseFilterByPermanence(SimpleListFilter): ...@@ -197,21 +196,24 @@ class PurchaseFilterByPermanence(SimpleListFilter):
class LUT_ProductionModeAdmin(admin.ModelAdmin): class LUT_ProductionModeAdmin(admin.ModelAdmin):
list_display = ('short_name', 'is_active') list_display = ('short_name', 'is_active')
list_display_links = ('short_name',) list_display_links = ('short_name',)
list_max_show_all = True list_per_page = 17
list_max_show_all = 17
admin.site.register(LUT_ProductionMode, LUT_ProductionModeAdmin) admin.site.register(LUT_ProductionMode, LUT_ProductionModeAdmin)
class LUT_DepartmentForCustomerAdmin(admin.ModelAdmin): class LUT_DepartmentForCustomerAdmin(admin.ModelAdmin):
list_display = ('short_name', 'is_active') list_display = ('short_name', 'is_active')
list_display_links = ('short_name',) list_display_links = ('short_name',)
list_max_show_all = True list_per_page = 17
list_max_show_all = 17
admin.site.register(LUT_DepartmentForCustomer, LUT_DepartmentForCustomerAdmin) admin.site.register(LUT_DepartmentForCustomer, LUT_DepartmentForCustomerAdmin)
class LUT_PermanenceRoleAdmin(admin.ModelAdmin): class LUT_PermanenceRoleAdmin(admin.ModelAdmin):
list_display = ('short_name', 'is_active') list_display = ('short_name', 'is_active')
list_display_links = ('short_name',) list_display_links = ('short_name',)
list_max_show_all = True list_per_page = 17
list_max_show_all = 17
admin.site.register(LUT_PermanenceRole, LUT_PermanenceRoleAdmin) admin.site.register(LUT_PermanenceRole, LUT_PermanenceRoleAdmin)
...@@ -220,22 +222,23 @@ class ProducerAdmin(admin.ModelAdmin): ...@@ -220,22 +222,23 @@ class ProducerAdmin(admin.ModelAdmin):
('short_profile_name', 'long_profile_name'), ('short_profile_name', 'long_profile_name'),
('email', 'fax'), ('email', 'fax'),
('phone1', 'phone2',), ('phone1', 'phone2',),
'order_description', # 'order_description',
'invoice_description', # 'invoice_description',
('price_list_multiplier','vat_level'), ('price_list_multiplier', 'vat_level'),
('date_balance', 'balance'), ('initial_balance', 'date_balance', 'balance'),
'represent_this_buyinggroup', ('invoice_by_basket', 'represent_this_buyinggroup'),
'address', 'address',
'is_active'] 'is_active']
readonly_fields = ( readonly_fields = (
'represent_this_buyinggroup', # 'represent_this_buyinggroup',
'date_balance', 'date_balance',
'balance', 'balance',
) )
search_fields = ('short_profile_name', 'email') search_fields = ('short_profile_name', 'email')
list_display = ('short_profile_name', 'get_products', 'get_balance', 'phone1', 'email', 'represent_this_buyinggroup', list_display = ('short_profile_name', 'get_products', 'get_balance', 'phone1', 'email', 'represent_this_buyinggroup',
'is_active') 'is_active')
list_max_show_all = True list_per_page = 17
list_max_show_all = 17
actions = [ actions = [
'export_xlsx', 'export_xlsx',
'import_xlsx', 'import_xlsx',
...@@ -249,6 +252,7 @@ class ProducerAdmin(admin.ModelAdmin): ...@@ -249,6 +252,7 @@ class ProducerAdmin(admin.ModelAdmin):
return import_product_xlsx(self, admin, request, queryset) return import_product_xlsx(self, admin, request, queryset)
import_xlsx.short_description = _("Import products of selected producer(s) from a XLSX file") import_xlsx.short_description = _("Import products of selected producer(s) from a XLSX file")
# def get_producer_phone1(self, obj): # def get_producer_phone1(self, obj):
# if obj.producer: # if obj.producer:
# return '%s'%(obj.producer.phone1) # return '%s'%(obj.producer.phone1)
...@@ -382,17 +386,18 @@ class CustomerWithUserDataAdmin(admin.ModelAdmin): ...@@ -382,17 +386,18 @@ class CustomerWithUserDataAdmin(admin.ModelAdmin):
('email','email2'), ('email','email2'),
('phone1', 'phone2'), ('phone1', 'phone2'),
'address', 'vat_id', 'address', 'vat_id',
('date_balance', 'balance'), ('initial_balance', 'date_balance', 'balance'),
('represent_this_buyinggroup', 'may_order','is_active') ('represent_this_buyinggroup', 'may_order','is_active')
] ]
readonly_fields = ( readonly_fields = (
'represent_this_buyinggroup', # 'represent_this_buyinggroup',
'date_balance', 'date_balance',
'balance', 'balance',
) )
search_fields = ('short_basket_name', 'user__email', 'email2') search_fields = ('short_basket_name', 'user__email', 'email2')
list_display = ('__unicode__', 'get_balance', 'may_order', 'phone1', 'phone2', 'get_email', 'email2') list_display = ('__unicode__', 'get_balance', 'may_order', 'phone1', 'phone2', 'get_email', 'email2', 'represent_this_buyinggroup')
list_max_show_all = True list_per_page = 17
list_max_show_all = 17
def get_email(self, obj): def get_email(self, obj):
if obj.user: if obj.user:
...@@ -449,7 +454,9 @@ class StaffWithUserDataAdmin(admin.ModelAdmin): ...@@ -449,7 +454,9 @@ class StaffWithUserDataAdmin(admin.ModelAdmin):
'is_reply_to_order_email', 'is_reply_to_invoice_email', 'is_reply_to_order_email', 'is_reply_to_invoice_email',
'customer_responsible','long_name', 'function_description', 'is_active'] 'customer_responsible','long_name', 'function_description', 'is_active']
list_display = ('__unicode__', 'customer_responsible', 'get_customer_phone1', 'is_active') list_display = ('__unicode__', 'customer_responsible', 'get_customer_phone1', 'is_active')
list_max_show_all = True list_select_related = ('customer_responsible',)
list_per_page = 17
list_max_show_all = 17
def get_form(self,request, obj=None, **kwargs): def get_form(self,request, obj=None, **kwargs):
form = super(StaffWithUserDataAdmin,self).get_form(request, obj, **kwargs) form = super(StaffWithUserDataAdmin,self).get_form(request, obj, **kwargs)
...@@ -494,36 +501,37 @@ class StaffWithUserDataAdmin(admin.ModelAdmin): ...@@ -494,36 +501,37 @@ class StaffWithUserDataAdmin(admin.ModelAdmin):
admin.site.register(Staff, StaffWithUserDataAdmin) admin.site.register(Staff, StaffWithUserDataAdmin)
class ProductAdmin(SortableAdminMixin, admin.ModelAdmin): class ProductAdmin(admin.ModelAdmin):
list_display = ('producer', list_display = (
'is_into_offer', 'is_into_offer',
'long_name', 'producer',
'department_for_customer', 'department_for_customer',
'long_name',
'original_unit_price', 'original_unit_price',
'unit_deposit', 'unit_deposit',
'customer_alert_order_quantity', 'customer_alert_order_quantity',
'get_order_unit',
'is_active') 'is_active')
list_display_links = ('long_name',) list_display_links = ('long_name',)
list_editable = ('original_unit_price',) list_editable = ('original_unit_price',)
readonly_fields = ('is_created_on', readonly_fields = ('is_created_on',
'is_updated_on') 'is_updated_on')
fields = ( fields = (
('producer', 'long_name'), ('producer', 'long_name', 'picture'),
('original_unit_price', 'unit_deposit', 'order_average_weight'), ('original_unit_price', 'unit_deposit', 'vat_level'),
# ('order_by_kg_pay_by_kg', 'order_by_piece_pay_by_piece', 'order_by_piece_pay_by_kg', 'producer_must_give_order_detail_per_customer', 'automatically_added'),
# 'usage_description',
('order_unit', 'order_average_weight', 'customer_minimum_order_quantity', 'customer_increment_order_quantity', 'customer_alert_order_quantity'),
('production_mode', 'department_for_customer', 'placement'),
'offer_description', 'offer_description',
'usage_description',
('order_by_kg_pay_by_kg', 'order_by_piece_pay_by_piece', 'order_by_piece_pay_by_kg', 'producer_must_give_order_detail_per_customer'),
('customer_minimum_order_quantity', 'customer_increment_order_quantity'),
'customer_alert_order_quantity',
('production_mode', 'picture'),
('department_for_customer', 'placement'),
('vat_level', 'automatically_added'),
('is_into_offer', 'is_active', 'is_created_on', 'is_updated_on') ('is_into_offer', 'is_active', 'is_created_on', 'is_updated_on')
) )
list_max_show_all = True list_select_related = ('producer', 'department_for_customer')
# ordering = ('producer', list_per_page = 100
# 'department_for_customer', list_max_show_all = 100
# 'long_name',) ordering = ('producer',
'department_for_customer',
'long_name',)
search_fields = ('long_name',) search_fields = ('long_name',)
list_filter = ('is_active', list_filter = ('is_active',
'is_into_offer', 'is_into_offer',
...@@ -531,6 +539,10 @@ class ProductAdmin(SortableAdminMixin, admin.ModelAdmin): ...@@ -531,6 +539,10 @@ class ProductAdmin(SortableAdminMixin, admin.ModelAdmin):
ProductFilterByProducer,) ProductFilterByProducer,)
actions = ['flip_flop_select_for_offer_status', 'duplicate_product' ] actions = ['flip_flop_select_for_offer_status', 'duplicate_product' ]
def get_order_unit(self, obj):
return obj.get_order_unit_display()
get_order_unit.short_description = _("order unit")
def flip_flop_select_for_offer_status(self, request, queryset): def flip_flop_select_for_offer_status(self, request, queryset):
for product in queryset.order_by(): for product in queryset.order_by():
product.is_into_offer = not product.is_into_offer product.is_into_offer = not product.is_into_offer
...@@ -540,13 +552,37 @@ class ProductAdmin(SortableAdminMixin, admin.ModelAdmin): ...@@ -540,13 +552,37 @@ class ProductAdmin(SortableAdminMixin, admin.ModelAdmin):
'flip_flop_select_for_offer_status for offer') 'flip_flop_select_for_offer_status for offer')
def duplicate_product(self, request, queryset): def duplicate_product(self, request, queryset):
user_message = _("The product is duplicated.")
user_message_level = messages.INFO
product_count = 0
duplicate_count = 0
for product in queryset: for product in queryset:
super(ProductAdmin,self).move_for_duplicate(product) product_count += 1
long_name_prefix = _("COPY_OF_") long_name_postfix = unicode(_(" (COPY)"))
max_length = Product._meta.get_field('long_name').max_length max_length = Product._meta.get_field('long_name').max_length - len(long_name_postfix)
product.long_name = cap(long_name_prefix + product.long_name, max_length) product.long_name = cap(product.long_name, max_length).decode("utf8") + long_name_postfix
product.id = None product_set = Product.objects.filter(
product.save() producer_id = product.producer_id,
long_name = product.long_name).order_by()[:1]
if product_set:
# avoid to break the unique index : producer_id, long_name
pass
else:
product.id = None
product.save()
duplicate_count += 1
if product_count == duplicate_count:
if product_count > 1:
user_message = _("The products are duplicated.")
else:
if product_count == 1:
user_message = _("The product has not been duplicated because a product with the same long name already exists.")
user_message_level = messages.ERROR
else:
user_message = _("At least one product has not been duplicated because a product with the same long name already exists.")
user_message_level = messages.WARNING
self.message_user(request, user_message, user_message_level)
duplicate_product.short_description = _('duplicate product') duplicate_product.short_description = _('duplicate product')
...@@ -554,6 +590,7 @@ class ProductAdmin(SortableAdminMixin, admin.ModelAdmin): ...@@ -554,6 +590,7 @@ class ProductAdmin(SortableAdminMixin, admin.ModelAdmin):
form = super(ProductAdmin,self).get_form(request, obj, **kwargs) form = super(ProductAdmin,self).get_form(request, obj, **kwargs)
# If we are coming from a list screen, use the filter to pre-fill the form # If we are coming from a list screen, use the filter to pre-fill the form
# print form.base_fields
producer = form.base_fields["producer"] producer = form.base_fields["producer"]
department_for_customer = form.base_fields["department_for_customer"] department_for_customer = form.base_fields["department_for_customer"]
production_mode = form.base_fields["production_mode"] production_mode = form.base_fields["production_mode"]
...@@ -630,7 +667,7 @@ class PermanenceBoardInline(admin.TabularInline): ...@@ -630,7 +667,7 @@ class PermanenceBoardInline(admin.TabularInline):
def formfield_for_foreignkey(self, db_field, request, **kwargs): def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "customer": if db_field.name == "customer":
kwargs["queryset"] = Customer.objects.all( kwargs["queryset"] = Customer.objects.all(
).active().not_the_buyinggroup() ).active() # .not_the_buyinggroup()
if db_field.name == "permanence_role": if db_field.name == "permanence_role":
kwargs["queryset"] = LUT_PermanenceRole.objects.all( kwargs["queryset"] = LUT_PermanenceRole.objects.all(
).active() ).active()
...@@ -679,12 +716,13 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin): ...@@ -679,12 +716,13 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin):
# ('status', 'automaticaly_closed'), # ('status', 'automaticaly_closed'),
'automaticaly_closed', 'automaticaly_closed',
'offer_description', 'offer_description',
'order_description', # 'order_description',
'producers' 'producers'
) )
# readonly_fields = ('status', 'is_created_on', 'is_updated_on') # readonly_fields = ('status', 'is_created_on', 'is_updated_on')
exclude = ['invoice_description'] exclude = ['invoice_description']
list_max_show_all = True list_per_page = 10
list_max_show_all = 10
filter_horizontal = ('producers',) filter_horizontal = ('producers',)
# inlines = [PermanenceBoardInline, OfferItemInline] # inlines = [PermanenceBoardInline, OfferItemInline]
inlines = [PermanenceBoardInline] inlines = [PermanenceBoardInline]
...@@ -698,6 +736,7 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin): ...@@ -698,6 +736,7 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin):
'close_and_send_orders', 'close_and_send_orders',
'delete_purchases', 'delete_purchases',
'back_to_planified', 'back_to_planified',
'generate_calendar'
] ]
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
...@@ -718,9 +757,10 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin): ...@@ -718,9 +757,10 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin):
def download_orders(self, request, queryset): def download_orders(self, request, queryset):
for permanence in queryset[:1]: for permanence in queryset[:1]:
if permanence.status==PERMANENCE_OPENED: if permanence.status >=PERMANENCE_OPENED:
response = HttpResponse(mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response = HttpResponse(mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=' + unicode(_('Check')) + '.xlsx' filename = (unicode(_("Check")) + u" - " + permanence.__unicode__() + u'.xlsx').encode('latin-1', errors='ignore')
response['Content-Disposition'] = 'attachment; filename=' + filename
wb = export_orders_xlsx(permanence) wb = export_orders_xlsx(permanence)
wb.save(response) wb.save(response)
return response return response
...@@ -733,25 +773,27 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin): ...@@ -733,25 +773,27 @@ class PermanenceInPreparationAdmin(admin.ModelAdmin):
current_site = get_current_site(request) current_site = get_current_site(request)
user_message = _("The status of this permanence prohibit you to open and send offers.") user_message = _("The status of this permanence prohibit you to open and send offers.")
user_message_level = messages.ERROR user_message_level = messages.ERROR
now = timezone.now()
for permanence in queryset[:1]: for permanence in queryset[:1]:
if permanence.status==PERMANENCE_PLANIFIED: if permanence.status==PERMANENCE_PLANIFIED:
permanence.status = PERMANENCE_WAIT_FOR_OPEN permanence.status = PERMANENCE_WAIT_FOR_OPEN
permanence.save(update_fields=['status']) permanence.is_updated_on = now
thread.start_new_thread( open_offers_async, (permanence.id, current_site.name) ) permanence.save(update_fields=['status','is_updated_on'])
thread.start_new_thread( tasks.open_offers, (permanence.id, current_site.name) )
user_message = _("The offers are being generated.") user_message = _("The offers are being generated.")
user_message_level = messages.INFO user_message_level = messages.INFO
elif permanence.status==PERMANENCE_WAIT_FOR_OPEN: elif permanence.status==PERMANENCE_WAIT_FOR_OPEN:
# On demand 15 minutes after the previous attempt, go back to previous status and send alert email # On demand 15 minutes after the previous attempt, go back to previous status and send alert email
now = datetime.datetime.utcnow().replace(tzinfo=utc) # use only timediff, -> timezone conversion not needed
timediff = now - permanence.is_updated_on timediff = now - permanence.is_updated_on
if timediff.total_seconds() > (15 * 60): if timediff.total_seconds() > (30 * 60):
send_alert_email(permanence, current_site.name) thread.start_new_thread( send_alert_email, (permanence, current_site.name) )
permanence.status = PERMANENCE_PLANIFIED permanence.status = PERMANENCE_PLANIFIED
permanence.save(update_fields=['status']) permanence.save(update_fields=['status'])
user_message = _("The action has been canceled by the system and an email send to the site administrator.") user_message = _("The action has been canceled by the system and an email send to the site administrator.")
user_message_level = messages.INFO user_message_level = messages.WARNING