├── .dockerignore ├── .github └── workflows │ └── pythonapp.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.markdown ├── billing ├── __init__.py ├── admin.py ├── factories.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20160228_1841.py │ ├── 0003_add_bill_detail_and_bill_expense.py │ ├── 0004_allow_null_in_bill_detail.py │ ├── 0005_auto_20171029_2326.py │ ├── 0006_auto_20180713_1851.py │ ├── 0007_auto_20180717_1636.py │ ├── 0008_billexpense_amount.py │ ├── 0009_auto_20180810_1715.py │ ├── 0010_clientbill_anonymize_profile.py │ ├── 0011_auto_20180905_1940.py │ ├── 0012_clientbill_lang.py │ ├── 0013_auto_20180909_2001.py │ ├── 0014_auto_20181120_1518.py │ ├── 0015_clientbill_include_timesheet.py │ ├── 0016_auto_20190303_2327.py │ ├── 0017_auto_20190308_1859.py │ ├── 0018_clientbill_client_comment.py │ ├── 0019_clientbill_client_deal_id.py │ ├── 0020_auto_20190819_1519.py │ ├── 0021_auto_20201212_1430.py │ ├── 0022_auto_20201213_1508.py │ ├── 0023_clientbill_allow_duplicate_expense.py │ ├── 0024_clientbill_billing_cli_state_e1a3c4_idx.py │ ├── 0025_clientbill_add_facturx_data.py │ ├── 0026_alter_clientbill_expenses_and_more.py │ └── __init__.py ├── models.py ├── tables.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── bot ├── declare_timesheet.py ├── pydici_bot.py └── utils.py ├── core ├── __init__.py ├── admin.py ├── context_processors.py ├── decorator.py ├── fixtures │ ├── auth.json │ ├── billing.json │ ├── crm.json │ ├── leads.json │ ├── people.json │ └── staffing.json ├── forms.py ├── management │ └── commands │ │ └── create_test_data.py ├── middleware.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_parameter.py │ ├── 0003_fiscal_year.py │ ├── 0004_fix_key.py │ ├── 0005_auto_20180909_1822.py │ ├── 0006_auto_20191206_1734.py │ ├── 0007_auto_20201212_1430.py │ ├── 0008_add_bot_interval.py │ ├── 0009_add_bot_timesheet_time.py │ ├── 0010_add_subcontractor_budget_margin.py │ ├── 0011_alter_groupfeature_feature.py │ ├── 0012_create_default_periodic_tasks.py │ ├── 0013_alter_groupfeature_feature.py │ ├── 0014_fix_periodict_tasks_scheduling.py │ ├── 0015_drop_actionset.py │ ├── 0016_view_warmup_scheduling.py │ └── __init__.py ├── models.py ├── tasks.py ├── templatetags │ ├── __init__.py │ ├── pydici_filters.py │ └── pydici_tags.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── crm ├── __init__.py ├── admin.py ├── factories.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── 0003_add_address_and_billing_adress.py │ ├── 0004_subsidiary_payment_description.py │ ├── 0005_auto_20180713_1914.py │ ├── 0006_subsidiary_commercial_name.py │ ├── 0007_client_billing_name.py │ ├── 0008_client_billing_contact.py │ ├── 0009_auto_20180928_1709.py │ ├── 0010_auto_20181002_0010.py │ ├── 0011_auto_20181120_1518.py │ ├── 0012_businessbroker_billing_name.py │ ├── 0013_auto_20200331_2307.py │ ├── 0014_auto_20201212_1430.py │ ├── 0015_client_billing_lang.py │ ├── 0016_auto_20201213_1508.py │ ├── 0017_auto_20221210_1715.py │ ├── 0018_auto_20221210_1725.py │ ├── 0019_remove_client_vat_id.py │ ├── 0020_auto_20221210_1800.py │ ├── 0021_auto_20221211_1710.py │ ├── 0022_auto_20221211_2156.py │ ├── 0023_auto_20230326_1749.py │ ├── 0024_alter_businesssector_options.py │ ├── 0025_alter_company_businessowner.py │ └── __init__.py ├── models.py ├── tables.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── data └── documents │ └── README ├── docker-compose.yml ├── expense ├── __init__.py ├── admin.py ├── forms.py ├── management │ └── __init__.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20181120_1518.py │ ├── 0003_expense_state.py │ ├── 0004_auto_20190113_2220.py │ ├── 0005_auto_20190113_2250.py │ ├── 0006_expense_vat.py │ └── __init__.py ├── models.py ├── tables.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── i18n.sh ├── leads ├── __init__.py ├── admin.py ├── factories.py ├── forms.py ├── learn.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_fix_description_for_markdown.py │ ├── 0003_lead_administrative_notes.py │ ├── 0004_auto_20181120_1518.py │ ├── 0005_auto_20190819_1519.py │ ├── 0006_add_warmup_periodic_task.py │ ├── 0007_alter_lead_business_broker_and_more.py │ └── __init__.py ├── models.py ├── tables.py ├── tasks.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── locale └── fr │ └── LC_MESSAGES │ ├── django.mo │ └── django.po ├── manage.py ├── media ├── css │ ├── ajax_select.css │ ├── billboard.min.css │ ├── images │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ └── ui-icons_cd0a0a_256x240.png │ ├── jquery.autocomplete.css │ ├── pydici-pdf.css │ ├── pydici.css │ └── subtotal.min.css ├── fonts │ └── circles │ │ ├── circles-webfont.eot │ │ ├── circles-webfont.svg │ │ ├── circles-webfont.ttf │ │ └── circles-webfont.woff ├── images │ ├── Sorting icons.psd │ ├── back_disabled.png │ ├── back_enabled.png │ ├── back_enabled_hover.png │ ├── forward_disabled.png │ ├── forward_enabled.png │ ├── forward_enabled_hover.png │ ├── loading-indicator.gif │ ├── sort_asc.png │ ├── sort_asc_disabled.png │ ├── sort_both.png │ ├── sort_desc.png │ └── sort_desc_disabled.png ├── img │ ├── ajax-loader.gif │ ├── changelist-bg.gif │ ├── default-bg-reverse.gif │ ├── default-bg.gif │ ├── gis │ │ ├── move_vertex_off.png │ │ └── move_vertex_on.png │ ├── icon-no.gif │ ├── icon-unknown.gif │ ├── icon-yes.gif │ ├── icon_addlink.gif │ ├── icon_alert.gif │ ├── icon_calendar.gif │ ├── icon_changelink.gif │ ├── icon_clock.gif │ ├── icon_deletelink.gif │ ├── icon_error.gif │ ├── icon_success.gif │ ├── nav-bg-reverse.gif │ ├── nav-bg-selected.gif │ ├── nav-bg.gif │ ├── sorting-icons.gif │ ├── tool-left.gif │ ├── tool-left_over.gif │ ├── tool-right.gif │ ├── tool-right_over.gif │ ├── tooltag-add.gif │ ├── tooltag-add_over.gif │ ├── tooltag-arrowright.gif │ └── tooltag-arrowright_over.gif ├── js │ ├── admin │ │ └── RelatedObjectLookups.js │ ├── billboard.min.js │ ├── d3-v7.8.2.min.js │ ├── d3.v3.min.js │ ├── dagre-d3.min.js │ ├── datatables_custom_sort.js │ ├── getElementsBySelector.js │ ├── htmx-2.0.3.min.js │ ├── jquery-3.7.0.min.js │ ├── jquery-ui-1.12.1.min.js │ ├── jquery.autocomplete.min.js │ ├── jquery.init.js │ ├── jquery.jeditable.mini.js │ ├── select2_locale_fr.js │ └── subtotal.min.js ├── pydici │ ├── README │ ├── company_logo.png │ ├── configure.png │ ├── decrease.png │ ├── fd.png │ ├── fg.png │ ├── header.png │ ├── increase.png │ ├── lead.png │ ├── menu │ │ ├── corner_inset_left.png │ │ ├── corner_inset_right.png │ │ ├── corner_left.png │ │ ├── corner_right.png │ │ ├── dot.png │ │ └── magnifier.png │ └── receipt.png └── tables2 │ ├── css │ └── screen.css │ └── img │ ├── arrow-active-down.png │ ├── arrow-active-up.png │ ├── arrow-inactive-down.png │ ├── arrow-inactive-up.png │ ├── false.gif │ ├── header-bg.png │ ├── missing.png │ ├── pagination-bg.gif │ └── true.gif ├── people ├── __init__.py ├── admin.py ├── api.py ├── factories.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_rename_rateobjective_daily_rate_field_to_rate.py │ ├── 0003_add_objective_rate_type.py │ ├── 0004_auto_20170817_2015.py │ ├── 0005_auto_20181120_1518.py │ ├── 0006_auto_20190310_2209.py │ ├── 0007_consultant_telegram_alias.py │ ├── 0008_consultant_telegram_id.py │ ├── 0009_compute_consultants_tasks_periodic_task.py │ ├── 0010_alter_consultant_telegram_id.py │ └── __init__.py ├── models.py ├── tasks.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── pydici ├── __init__.py ├── pydici_celery.py ├── settings │ ├── __init__.py │ ├── dev.py │ ├── django.py │ ├── prod.py │ └── pydici.py ├── urls.py └── wsgi.py ├── requirements-dev.txt ├── requirements-sklearn.txt ├── requirements.txt ├── run-dev-server.sh ├── run-test.sh ├── scripts ├── check_signed_pdf.py ├── export_users.py ├── extract_objectives.py ├── flush.sql └── import_objectives.py ├── staffing ├── __init__.py ├── admin.py ├── api.py ├── factories.py ├── forms.py ├── lookups.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20160214_1402.py │ ├── 0003_auto_20160228_1841.py │ ├── 0004_auto_20181120_1518.py │ ├── 0005_auto_20190819_1519.py │ ├── 0006_auto_20191206_1734.py │ ├── 0007_auto_20191206_1752.py │ ├── 0008_auto_20191206_2006.py │ ├── 0009_auto_20200227_1731.py │ ├── 0010_mission_management_mode.py │ ├── 0011_auto_20200302_1457.py │ ├── 0012_auto_20210116_1825.py │ ├── 0013_auto_20220826_1752.py │ ├── 0014_mission_min_charge_per_day.py │ ├── 0015_alter_mission_management_mode.py │ ├── 0016_auto_20230303_1646.py │ ├── 0017_alter_staffing_options.py │ ├── 0017_auto_20230307_0000.py │ ├── 0018_merge_20230310_1735.py │ ├── 0019_auto_20230607_2253.py │ ├── 0020_alter_mission_min_charge_multiple_per_day.py │ ├── 0021_mission_client_deal_id.py │ ├── 0022_alter_mission_responsible.py │ ├── 0023_mission_always_displayed.py │ ├── 0024_staffing_staffing_st_mission_2c479a_idx.py │ └── __init__.py ├── models.py ├── optim.py ├── tables.py ├── tasks.py ├── tests.py ├── urls.py ├── utils.py └── views.py └── templates ├── 404.html ├── 500.html ├── admin └── base_site.html ├── batch └── timesheet_warning_email.txt ├── billing ├── _lead_billing.html ├── bill.html ├── bill_review.html ├── client_bill_detail.html ├── client_bill_form.html ├── client_billing_control_pivotable.html ├── client_bills_archive.html ├── client_bills_in_creation.html ├── graph_billing.html ├── graph_outstanding_billing.html ├── graph_yearly_billing.html ├── invoice-factur-x.xml ├── payment_delay.html ├── pre_billing.html ├── supplier_bill_form.html ├── supplier_bills_archive.html └── supplier_bills_validation.html ├── core ├── _access_forbidden.html ├── _ajax_popup_post.html ├── _ajax_post.html ├── _billboard.html ├── _color_timesheet.html ├── _datatables-dj-tables.html ├── _datatables.html ├── _date_column.html ├── _datepicker.html ├── _form.html ├── _highlight_column.html ├── _init_tabs.html ├── _messages.html ├── _object_history.html ├── _pivotable_body.html ├── _pivotable_header.html ├── _pydici_menu.html ├── _select2.html ├── dashboard.html ├── delete.html ├── forbidden.html ├── form.html ├── index.html ├── pydici.html ├── risks.html └── search.html ├── crm ├── __leads_state_donut.html ├── _client_picto.html ├── _clientcompany_billing.html ├── _clientcompany_list.html ├── _clientcompany_rates_margin.html ├── _contact_item.html ├── _contact_list.html ├── _mission_contact_form.html ├── businessbroker_list.html ├── client-popup.html ├── client.html ├── client_organisation.html ├── clientcompany.html ├── clientcompany_detail.html ├── clientcompany_list.html ├── clientcompany_ranking.html ├── company_pivotable.html ├── contact_detail.html ├── contact_list.html ├── graph_company_business_activity.html ├── graph_company_sales.html └── supplier_list.html ├── expense ├── _expense_chargeable.html ├── _expense_receipt_modal.html ├── _expense_state_column.html ├── _expense_transitions_column.html ├── _expense_vat_column.html ├── _receipt_column.html ├── chargeable_expenses.html ├── expense.html ├── expense_archive.html ├── expense_list.html ├── expense_payment_detail.html ├── expense_payments.html └── expenses.html ├── leads ├── IA_stats.html ├── __lead_name.html ├── _lead_checkdoc.html ├── _state_column.html ├── _tag_cloud.html ├── _tags_banner.html ├── feed_title.txt ├── graph_leads_activity.html ├── graph_leads_bar.html ├── graph_leads_pipe.html ├── graph_won_rate.html ├── lead.html ├── lead_detail.html ├── lead_documents.html ├── lead_mail.html ├── lead_mail.txt ├── lead_pivotable.html ├── leads.html ├── leads_pivotable.html ├── leads_to_bill.html ├── mail.html ├── mail.txt ├── manage_tags.html ├── review.html └── tag.html ├── people ├── __consultant_name.html ├── consultant.html ├── consultant_achievements.html ├── consultant_detail.html ├── consultants_tasks.html ├── graph_people_count.html └── subcontractor_detail.html └── staffing ├── _check_mission_billing_mode.html ├── _check_mission_margin.html ├── _check_mission_rates.html ├── _check_prod_mission.html ├── _consultant_prod_tooltip.html ├── _mission_consultants_rate.html ├── _mission_table.html ├── _mission_table_archive_column.html ├── _staffing_fold.html ├── all_timesheet.html ├── consultant_staffing.html ├── consultant_timesheet.html ├── feed_staffing_content.html ├── feed_staffing_title.txt ├── fixed_price_report.html ├── graph_consultant_rate.html ├── graph_profile_rates.html ├── graph_timesheet_rates_bar.html ├── holidays_planning.html ├── lunch_tickets_pivotable.html ├── mass_staffing.html ├── mission.html ├── mission_consultants.html ├── mission_contacts.html ├── mission_staffing.html ├── mission_timesheet.html ├── mission_timesheet_report.html ├── missions.html ├── missions_report.html ├── optimise_pdc.html ├── pdc_detail.html ├── pdc_review.html ├── prod_report.html ├── rate_objective_report.html ├── rates_report.html └── turnover_pivotable.html /.dockerignore: -------------------------------------------------------------------------------- 1 | data 2 | -------------------------------------------------------------------------------- /.github/workflows/pythonapp.yml: -------------------------------------------------------------------------------- 1 | name: pydici test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | check_migrations: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-python@v5 11 | with: 12 | python-version: '3.11' 13 | - name: Install dependencies 14 | run: | 15 | python -m pip install --upgrade pip 16 | pip install -r requirements.txt 17 | - name: Check migrations 18 | run: | 19 | echo "SECRET_KEY='test'" > ./pydici/settings/local.py 20 | ./manage.py makemigrations --check 21 | build_and_test: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Launch pydici (mariadb, memcached, redis, django and celery) 26 | run: docker compose up -d 27 | - name: setup fs right 28 | run: sudo chown -R $USER:$USER data && sudo chmod 777 -R data 29 | - name: Run tests 30 | run: docker exec -e RUNNING_IN_GH=1 pydici-django-1 /code/run-test.sh 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.db 3 | .*.swp 4 | .py* 5 | .pydevproject 6 | .idea/ 7 | .project 8 | .settings 9 | .ipynb_checkpoints/ 10 | static/ 11 | venv/ 12 | venv3/ 13 | data/* 14 | !data/documents/ 15 | data/documents/* 16 | !data/documents/README 17 | node_modules/ 18 | .DS_Store 19 | .vscode/ 20 | notebooks/ 21 | celerybeat-schedule 22 | package-lock.json 23 | dump.rdb 24 | pydici/settings/local.py 25 | media/favicon-* 26 | media/pydici/company_logo-* 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM python:3.11 3 | RUN apt-get update && apt-get -y install gettext chromium ghostscript 4 | # Install pydici django env 5 | RUN useradd -m pydici 6 | USER pydici 7 | ENV PATH="/home/pydici/.local/bin/:$PATH" 8 | ENV PYTHONDONTWRITEBYTECODE=1 9 | ENV PYTHONUNBUFFERED=1 10 | WORKDIR /code 11 | COPY requirements*txt /code/ 12 | RUN pip install --upgrade pip 13 | RUN pip install -r requirements.txt 14 | RUN pip install -r requirements-sklearn.txt 15 | RUN pip install -r requirements-dev.txt 16 | 17 | -------------------------------------------------------------------------------- /billing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Pydici billing management module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /billing/migrations/0002_auto_20160228_1841.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | import datetime 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='clientbill', 17 | name='creation_date', 18 | field=models.DateField(default=datetime.date.today, verbose_name='Creation date'), 19 | ), 20 | migrations.AlterField( 21 | model_name='supplierbill', 22 | name='creation_date', 23 | field=models.DateField(default=datetime.date.today, verbose_name='Creation date'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /billing/migrations/0004_allow_null_in_bill_detail.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0003_add_bill_detail_and_bill_expense'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='billdetail', 16 | name='consultant', 17 | field=models.ForeignKey(blank=True, to='people.Consultant', null=True, on_delete=models.deletion.CASCADE), 18 | ), 19 | migrations.AlterField( 20 | model_name='billdetail', 21 | name='quantity', 22 | field=models.FloatField(null=True, verbose_name='Quantity', blank=True), 23 | ), 24 | migrations.AlterField( 25 | model_name='billdetail', 26 | name='unit_price', 27 | field=models.DecimalField(null=True, verbose_name='Unit price (\u20ac)', max_digits=10, decimal_places=2, blank=True), 28 | ), 29 | migrations.AlterField( 30 | model_name='billdetail', 31 | name='vat', 32 | field=models.DecimalField(default=20.0, verbose_name='VAT (%)', max_digits=4, decimal_places=2), 33 | ), 34 | migrations.AlterField( 35 | model_name='billdetail', 36 | name='month', 37 | field=models.DateField(null=True), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /billing/migrations/0005_auto_20171029_2326.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0004_allow_null_in_bill_detail'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='billexpense', 16 | name='amount_with_vat', 17 | field=models.DecimalField(null=True, verbose_name='Amount (\u20ac incl tax)', max_digits=10, decimal_places=2, blank=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='billexpense', 21 | name='expense', 22 | field=models.ForeignKey(verbose_name='Expense', to='expense.Expense', on_delete=models.deletion.SET_NULL), 23 | ), 24 | migrations.AlterField( 25 | model_name='billexpense', 26 | name='expense_date', 27 | field=models.DateField(null=True, verbose_name='Expense date'), 28 | ), 29 | migrations.AlterField( 30 | model_name='billexpense', 31 | name='label', 32 | field=models.CharField(max_length=200, null=True, verbose_name='Description', blank=True), 33 | ), 34 | migrations.AlterField( 35 | model_name='clientbill', 36 | name='vat', 37 | field=models.DecimalField(default=20.0, verbose_name='VAT (%)', max_digits=4, decimal_places=2), 38 | ), 39 | migrations.AlterField( 40 | model_name='supplierbill', 41 | name='vat', 42 | field=models.DecimalField(default=20.0, verbose_name='VAT (%)', max_digits=4, decimal_places=2), 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /billing/migrations/0006_auto_20180713_1851.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | import billing.models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0005_auto_20171029_2326'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='clientbill', 17 | name='bill_id', 18 | field=models.CharField(default=billing.models.default_bill_id, unique=True, max_length=200, verbose_name='Bill id'), 19 | ), 20 | migrations.AlterField( 21 | model_name='supplierbill', 22 | name='bill_id', 23 | field=models.CharField(default=billing.models.default_bill_id, unique=True, max_length=200, verbose_name='Bill id'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /billing/migrations/0007_auto_20180717_1636.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0006_auto_20180713_1851'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='clientbill', 16 | name='state', 17 | field=models.CharField(default='0_DRAFT', max_length=30, verbose_name='State', choices=[('0_DRAFT', 'Draft'), ('0_PROPOSED', 'Proposed'), ('1_SENT', 'Sent'), ('2_PAID', 'Paid'), ('3_LITIGIOUS', 'Litigious'), ('4_CANCELED', 'Canceled')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0008_billexpense_amount.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0007_auto_20180717_1636'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='billexpense', 16 | name='amount', 17 | field=models.DecimalField(null=True, verbose_name='Amount (\u20ac excl tax)', max_digits=10, decimal_places=2, blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0009_auto_20180810_1715.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0008_billexpense_amount'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='billdetail', 16 | name='quantity', 17 | field=models.FloatField(default=1, verbose_name='Quantity'), 18 | preserve_default=False, 19 | ), 20 | migrations.AlterField( 21 | model_name='billdetail', 22 | name='unit_price', 23 | field=models.DecimalField(default=1000, verbose_name='Unit price (\u20ac)', max_digits=10, decimal_places=2), 24 | preserve_default=False, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /billing/migrations/0010_clientbill_anonymize_profile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0009_auto_20180810_1715'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='clientbill', 16 | name='anonymize_profile', 17 | field=models.BooleanField(default=False, verbose_name='Anonymize profile name'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0011_auto_20180905_1940.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0010_clientbill_anonymize_profile'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='billexpense', 16 | name='expense', 17 | field=models.ForeignKey(verbose_name='Expense', blank=True, to='expense.Expense', null=True, on_delete=models.deletion.SET_NULL), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0012_clientbill_lang.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0011_auto_20180905_1940'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='clientbill', 16 | name='lang', 17 | field=models.CharField(default='fr-fr', max_length=10, verbose_name='Lang'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0013_auto_20180909_2001.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0012_clientbill_lang'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='clientbill', 16 | name='lang', 17 | field=models.CharField(default='fr-fr', max_length=10, verbose_name='Language', choices=[('fr-fr', 'French'), ('en-en', 'English')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0014_auto_20181120_1518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-20 15:18 3 | 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('billing', '0013_auto_20180909_2001'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='billexpense', 18 | name='expense', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='expense.Expense', verbose_name='Expense'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /billing/migrations/0015_clientbill_include_timesheet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-02-04 21:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0014_auto_20181120_1518'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='clientbill', 17 | name='include_timesheet', 18 | field=models.BooleanField(default=False, verbose_name='Include timesheet'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /billing/migrations/0016_auto_20190303_2327.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-03-03 23:27 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0015_clientbill_include_timesheet'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='supplierbill', 17 | name='state', 18 | field=models.CharField(choices=[('1_RECEIVED', 'Received'), ('1_VALIDATED', 'Validated'), ('2_PAID', 'Paid'), ('3_LITIGIOUS', 'Litigious'), ('4_CANCELED', 'Canceled')], default='1_RECEIVED', max_length=30, verbose_name='State'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /billing/migrations/0017_auto_20190308_1859.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-03-08 18:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0016_auto_20190303_2327'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='clientbill', 17 | name='previous_year_bill', 18 | ), 19 | migrations.RemoveField( 20 | model_name='supplierbill', 21 | name='previous_year_bill', 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /billing/migrations/0018_clientbill_client_comment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-03-09 18:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0017_auto_20190308_1859'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='clientbill', 17 | name='client_comment', 18 | field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Client comments'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /billing/migrations/0019_clientbill_client_deal_id.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-03-09 19:16 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('billing', '0018_clientbill_client_comment'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='clientbill', 17 | name='client_deal_id', 18 | field=models.CharField(blank=True, max_length=100, verbose_name='Client deal id'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /billing/migrations/0020_auto_20190819_1519.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.5 on 2019-08-19 15:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0019_clientbill_client_deal_id'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='clientbill', 15 | name='expenses', 16 | field=models.ManyToManyField(blank=True, limit_choices_to={'chargeable': True}, to='expense.Expense'), 17 | ), 18 | migrations.AlterField( 19 | model_name='supplierbill', 20 | name='expenses', 21 | field=models.ManyToManyField(blank=True, limit_choices_to={'chargeable': True}, to='expense.Expense'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /billing/migrations/0021_auto_20201212_1430.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-12-12 14:30 2 | 3 | import billing.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0020_auto_20190819_1519'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='clientbill', 16 | name='bill_file', 17 | field=models.FileField(blank=True, max_length=500, null=True, storage=billing.models.BillStorage(nature='client'), upload_to=billing.models.bill_file_path, verbose_name='Bill File'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /billing/migrations/0022_auto_20201213_1508.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-12-13 15:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0021_auto_20201212_1430'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='clientbill', 15 | name='lang', 16 | field=models.CharField(blank=True, choices=[('fr-fr', 'French'), ('en-en', 'English')], max_length=10, null=True, verbose_name='Language'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /billing/migrations/0023_clientbill_allow_duplicate_expense.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.15 on 2022-11-28 18:24 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0022_auto_20201213_1508'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='clientbill', 15 | name='allow_duplicate_expense', 16 | field=models.BooleanField(default=False, verbose_name='Allow to bill twice an expense'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /billing/migrations/0024_clientbill_billing_cli_state_e1a3c4_idx.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-03 22:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0023_clientbill_allow_duplicate_expense'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddIndex( 14 | model_name='clientbill', 15 | index=models.Index(fields=['state', 'due_date'], name='billing_cli_state_e1a3c4_idx'), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /billing/migrations/0025_clientbill_add_facturx_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-05-07 16:49 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0024_clientbill_billing_cli_state_e1a3c4_idx'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='clientbill', 15 | name='add_facturx_data', 16 | field=models.BooleanField(default=False, verbose_name='Add Factur-X embedded information'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /billing/migrations/0026_alter_clientbill_expenses_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.16 on 2025-03-13 09:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('expense', '0006_expense_vat'), 10 | ('billing', '0025_clientbill_add_facturx_data'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='clientbill', 16 | name='expenses', 17 | field=models.ManyToManyField(blank=True, limit_choices_to={'chargeable': True}, to='expense.expense', verbose_name='expenses'), 18 | ), 19 | migrations.AlterField( 20 | model_name='supplierbill', 21 | name='expenses', 22 | field=models.ManyToManyField(blank=True, limit_choices_to={'chargeable': True}, to='expense.expense', verbose_name='expenses'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /billing/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/billing/migrations/__init__.py -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Pydici core module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /core/middleware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Pydici core middleware 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | 8 | 9 | class ScopeMiddleware: 10 | """Catch scope change request and update session accordingly""" 11 | def __init__(self, get_response): 12 | self.get_response = get_response 13 | 14 | 15 | def __call__(self, request): 16 | if "subsidiary_id" in request.GET: 17 | try: 18 | subsidiary_id = int(request.GET["subsidiary_id"]) 19 | except ValueError: 20 | subsidiary_id = 0 21 | request.session["subsidiary_id"] = subsidiary_id 22 | return self.get_response(request) 23 | -------------------------------------------------------------------------------- /core/migrations/0003_fiscal_year.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | def add_default_parameters(apps, schema_editor): 7 | Parameter = apps.get_model("core", "Parameter") 8 | Parameter.objects.bulk_create([ 9 | Parameter(key="FISCAL_YEAR_MONTH", value="1", type="FLOAT", 10 | desc="Fiscal year start. 1 for janury, 6 for june etc."), 11 | ]) 12 | 13 | 14 | def remove_default_parameters(apps, schema_editor): 15 | Parameter = apps.get_model("core", "Parameter") 16 | Parameter.objects.filter(key="FISCAL_YEAR_MONTH").delete() 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [ 21 | ('core', '0002_parameter'), 22 | ] 23 | 24 | operations = [ 25 | migrations.RunPython(add_default_parameters, remove_default_parameters), 26 | ] 27 | -------------------------------------------------------------------------------- /core/migrations/0004_fix_key.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | def add_default_parameters(apps, schema_editor): 7 | Parameter = apps.get_model("core", "Parameter") 8 | p = Parameter.objects.get(key="MAIL_FROM ") 9 | p.key = "MAIL_FROM" 10 | p.save() 11 | 12 | 13 | def remove_default_parameters(apps, schema_editor): 14 | Parameter = apps.get_model("core", "Parameter") 15 | Parameter = apps.get_model("core", "Parameter") 16 | p = Parameter.objects.get(key="MAIL_FROM") 17 | p.key = "MAIL_FROM " 18 | p.save() 19 | 20 | class Migration(migrations.Migration): 21 | 22 | dependencies = [ 23 | ('core', '0003_fiscal_year'), 24 | ] 25 | 26 | operations = [ 27 | migrations.RunPython(add_default_parameters, remove_default_parameters), 28 | ] 29 | -------------------------------------------------------------------------------- /core/migrations/0005_auto_20180909_1822.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('core', '0004_fix_key'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='groupfeature', 16 | name='feature', 17 | field=models.CharField(max_length=80, verbose_name='Feature', choices=[('3rdparties', "3rd Parties: Access to the 'Third parties' menu"), ('contacts_write', 'Contacts, write access: Allow adding, editing, removing contacts'), ('leads', "Leads: Access to the 'Leads' menu"), ('leads_list_all', "Leads, list all: Access to the 'Leads > All leads' menu entry"), ('leads_profitability', "Leads, profitability: Access to the 'Profitability' information in lead description"), ('management', "Management: Access to the 'Management' menu"), ('menubar', 'Menubar: Show the menubar'), ('reports', "Reports: Access to the 'Reports' menu"), ('search', 'Search: Allow searching'), ('staffing', 'Staffing: Access to staffing features'), ('staffing_mass', 'Staffing, mass edit: Access to mass staffing features'), ('timesheet_all', 'Timesheet, all: Access to all timesheets of all users'), ('timesheet_current_month', 'Timesheet, current month: Access to current month timesheets of all users'), ('billing_management', 'Manage bills, allow to mark bills sent, paid etc.'), ('billing_request', 'Create bills and proposed them to billing team ')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /core/migrations/0006_auto_20191206_1734.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-12-06 17:34 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('core', '0005_auto_20180909_1822'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='groupfeature', 15 | name='feature', 16 | field=models.CharField(choices=[('3rdparties', "3rd Parties: Access to the 'Third parties' menu"), ('contacts_write', 'Contacts, write access: Allow adding, editing, removing contacts'), ('leads', "Leads: Access to the 'Leads' menu"), ('leads_list_all', "Leads, list all: Access to the 'Leads > All leads' menu entry"), ('leads_profitability', "Leads, profitability: Access to the 'Profitability' information in lead description"), ('management', "Management: Access to the 'Management' menu"), ('menubar', 'Menubar: Show the menubar'), ('reports', "Reports: Access to the 'Reports' menu"), ('search', 'Search: Allow searching'), ('staffing', 'Staffing: Access to staffing features'), ('staffing_mass', 'Staffing, mass edit: Access to mass staffing features'), ('timesheet_all', 'Timesheet, all: Access to all timesheets of all users'), ('timesheet_current_month', 'Timesheet, current month: Access to current month timesheets of all users'), ('timesheet_subcontractor', 'Timesheet management of subcontractor'), ('billing_management', 'Manage bills, allow to mark bills sent, paid etc.'), ('billing_request', 'Create bills and proposed them to billing team '), ('expense', 'Expense request')], max_length=80, verbose_name='Feature'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /core/migrations/0008_add_bot_interval.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | def add_default_parameters(apps, schema_editor): 7 | Parameter = apps.get_model("core", "Parameter") 8 | Parameter.objects.bulk_create([ 9 | Parameter(key="BOT_ALERT_INTERVAL", value="1800", type="FLOAT", 10 | desc="Interval (in sec) between two warnings sent by pydici telegram bot"), 11 | ]) 12 | 13 | 14 | def remove_default_parameters(apps, schema_editor): 15 | Parameter = apps.get_model("core", "Parameter") 16 | Parameter.objects.filter(key="BOT_ALERT_INTERVAL").delete() 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [ 21 | ('core', '0007_auto_20201212_1430'), 22 | ] 23 | 24 | operations = [ 25 | migrations.RunPython(add_default_parameters, remove_default_parameters), 26 | ] 27 | -------------------------------------------------------------------------------- /core/migrations/0009_add_bot_timesheet_time.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def add_default_parameters(apps, schema_editor): 8 | Parameter = apps.get_model("core", "Parameter") 9 | Parameter.objects.bulk_create([ 10 | Parameter(key="BOT_CALL_TIME_FOR_TIMESHEET", value="18:45", type="TEXT", 11 | desc="Time (in format hours:min ex. 18:45) for calling people to declare their timesheet"), 12 | ]) 13 | 14 | 15 | def remove_default_parameters(apps, schema_editor): 16 | Parameter = apps.get_model("core", "Parameter") 17 | Parameter.objects.filter(key="BOT_CALL_TIME_FOR_TIMESHEET").delete() 18 | 19 | 20 | class Migration(migrations.Migration): 21 | dependencies = [ 22 | ('core', '0008_add_bot_interval'), 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(add_default_parameters, remove_default_parameters), 27 | ] 28 | -------------------------------------------------------------------------------- /core/migrations/0010_add_subcontractor_budget_margin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | def add_default_parameters(apps, schema_editor): 6 | Parameter = apps.get_model("core", "Parameter") 7 | Parameter.objects.bulk_create([ 8 | Parameter(key="SUBCONTRACTOR_BUDGET_MARGIN", value="15", type="FLOAT", 9 | desc="% (0-100) of objective margin for subcontractors."), 10 | ]) 11 | 12 | 13 | def remove_default_parameters(apps, schema_editor): 14 | Parameter = apps.get_model("core", "Parameter") 15 | Parameter.objects.filter(key="SUBCONTRACTOR_BUDGET_MARGIN").delete() 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [ 21 | ('core', '0009_add_bot_timesheet_time'), 22 | ] 23 | 24 | operations = [ 25 | migrations.RunPython(add_default_parameters, remove_default_parameters), 26 | ] 27 | -------------------------------------------------------------------------------- /core/migrations/0014_fix_periodict_tasks_scheduling.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from django.db import migrations, models 3 | from django.conf import settings 4 | 5 | from django_celery_beat.models import CrontabSchedule, PeriodicTask 6 | 7 | 8 | def update_scheduling(apps, schema_editor): 9 | c = CrontabSchedule.objects.get(minute=0, hour=6, day_of_week="0-5", 10 | day_of_month="*", month_of_year="*", 11 | timezone=settings.TIME_ZONE) 12 | c.day_of_month = "2-31" 13 | c.save() 14 | 15 | 16 | def undo_update_scheduling(apps, schema_editor): 17 | c = CrontabSchedule.objects.get(minute=0, hour=6, day_of_week="0-5", 18 | day_of_month="2-31", month_of_year="*", 19 | timezone=settings.TIME_ZONE) 20 | c.day_of_month = "*" 21 | c.save() 22 | 23 | 24 | class Migration(migrations.Migration): 25 | dependencies = [ 26 | ("core", "0013_alter_groupfeature_feature"), 27 | ] 28 | 29 | operations = [ 30 | migrations.RunPython(update_scheduling, undo_update_scheduling), 31 | ] 32 | -------------------------------------------------------------------------------- /core/migrations/0015_drop_actionset.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from django.db import migrations, models 3 | from django.conf import settings 4 | 5 | from django_celery_beat.models import CrontabSchedule, PeriodicTask 6 | 7 | 8 | DROP_ACTIONSET_TABLES = """ 9 | drop table if exists actionset_actionstate; 10 | drop table if exists actionset_action; 11 | drop table if exists actionset_actionset; 12 | """ 13 | 14 | 15 | class Migration(migrations.Migration): 16 | dependencies = [ 17 | ("core", "0014_fix_periodict_tasks_scheduling"), 18 | ] 19 | 20 | operations = [ 21 | migrations.RunSQL(DROP_ACTIONSET_TABLES), 22 | ] 23 | -------------------------------------------------------------------------------- /core/migrations/0016_view_warmup_scheduling.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from django.db import migrations, models 3 | from django.conf import settings 4 | 5 | from django_celery_beat.models import CrontabSchedule, PeriodicTask 6 | 7 | 8 | def add_warmup_task(apps, schema_editor): 9 | c, created = CrontabSchedule.objects.get_or_create(minute=0, hour=6, day_of_week="*", 10 | day_of_month="*", month_of_year="*", 11 | timezone=settings.TIME_ZONE) 12 | t = PeriodicTask.objects.create(name="view warmup", task="core.tasks.view_warmup", crontab=c) 13 | 14 | 15 | 16 | def remove_warmup_task(apps, schema_editor): 17 | t = PeriodicTask.objects.get(name="view warmup", task="core.tasks.view_warmup") 18 | t.delete() 19 | 20 | 21 | class Migration(migrations.Migration): 22 | dependencies = [ 23 | ("core", "0015_drop_actionset"), 24 | ("django_celery_beat", "0016_alter_crontabschedule_timezone") 25 | ] 26 | 27 | operations = [ 28 | migrations.RunPython(add_warmup_task, remove_warmup_task), 29 | ] 30 | -------------------------------------------------------------------------------- /core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/core/migrations/__init__.py -------------------------------------------------------------------------------- /core/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 4 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 5 | """ -------------------------------------------------------------------------------- /core/templatetags/pydici_tags.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | """ 3 | Pydici custom tags 4 | @author: Sébastien Renard 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | 8 | import re 9 | 10 | import markdown 11 | from markdown.extensions.sane_lists import SaneListExtension 12 | import bleach 13 | 14 | from django import template 15 | 16 | register = template.Library() 17 | 18 | 19 | @register.simple_tag(takes_context=True) 20 | def call_for_current_subsidiary(context, obj, f_name): 21 | """Allow calling method on object with current subsidiary as parameter. 22 | Used for model methods called on templates that do not have access to request and session and cannot be called 23 | with explicit arguments""" 24 | subsidiary = context.get("current_subsidiary") 25 | f = getattr(obj, f_name) 26 | if subsidiary: 27 | return f(subsidiary=subsidiary) 28 | else: 29 | return f() -------------------------------------------------------------------------------- /core/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """URL dispatcher for core module 3 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 4 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 5 | """ 6 | 7 | from django.urls import re_path 8 | import core.views as v 9 | 10 | core_urls = [ re_path(r'^$', v.index, name='index'), 11 | re_path(r'^search$', v.search, name='search'), 12 | re_path(r'^dashboard$', v.dashboard, name='dashboard'), 13 | re_path(r'^risks$', v.risk_reporting, name='risk_reporting'), 14 | re_path(r'^object_history/(?P[a-z]+)/(?P\d+)/$', v.object_history, name="object_history"), 15 | re_path(r'^forbidden$', v.forbidden, name='forbidden'), 16 | re_path(r'^financial-control//?$', v.financial_control, name="financial_control"), 17 | re_path(r'^financial-control/(?P\d{6})/?$', v.financial_control, name="financial_control"), 18 | re_path(r'^financial-control/(?P\d{6})/(?P\d{6})/?$', v.financial_control, name="financial_control") 19 | ] 20 | -------------------------------------------------------------------------------- /crm/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Customer management module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /crm/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Subsidiary', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('name', models.CharField(unique=True, max_length=200, verbose_name='Name')), 19 | ('code', models.CharField(unique=True, max_length=3, verbose_name='Code')), 20 | ('web', models.URLField(null=True, blank=True)), 21 | ], 22 | options={ 23 | 'ordering': ['name'], 24 | 'verbose_name': 'Subsidiary', 25 | 'verbose_name_plural': 'Subsidiaries', 26 | }, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /crm/migrations/0004_subsidiary_payment_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0003_add_address_and_billing_adress'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='subsidiary', 16 | name='payment_description', 17 | field=models.TextField(null=True, verbose_name='Payment condition description', blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /crm/migrations/0006_subsidiary_commercial_name.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0005_auto_20180713_1914'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='subsidiary', 16 | name='commercial_name', 17 | field=models.CharField(default='Please define commercial name of this subsidiary', max_length=200, verbose_name='Commercial name'), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /crm/migrations/0007_client_billing_name.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0006_subsidiary_commercial_name'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='client', 16 | name='billing_name', 17 | field=models.CharField(max_length=200, null=True, verbose_name='Name used for billing', blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /crm/migrations/0008_client_billing_contact.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0007_client_billing_name'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='client', 16 | name='billing_contact', 17 | field=models.ForeignKey(to='crm.AdministrativeContact', null=True, on_delete=models.SET_NULL), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /crm/migrations/0009_auto_20180928_1709.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0008_client_billing_contact'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='client', 16 | name='billing_contact', 17 | field=models.ForeignKey(verbose_name='Billing contact', blank=True, to='crm.AdministrativeContact', null=True, on_delete=models.SET_NULL), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /crm/migrations/0010_auto_20181002_0010.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('crm', '0009_auto_20180928_1709'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='client', 17 | name='billing_contact', 18 | field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, verbose_name='Billing contact', blank=True, to='crm.AdministrativeContact', null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /crm/migrations/0011_auto_20181120_1518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-20 15:18 3 | 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('crm', '0010_auto_20181002_0010'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='company', 18 | name='businessOwner', 19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='company_business_owner', to='people.Consultant', verbose_name='Business owner'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /crm/migrations/0012_businessbroker_billing_name.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.20 on 2019-05-04 10:57 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('crm', '0011_auto_20181120_1518'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='businessbroker', 17 | name='billing_name', 18 | field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Name used for billing'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /crm/migrations/0013_auto_20200331_2307.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2020-03-31 23:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0012_businessbroker_billing_name'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='client', 15 | name='expectations', 16 | field=models.CharField(choices=[('1_NONE', 'Aucune'), ('2_DECREASING', 'Décroissance'), ('3_FLAT', 'Inchangée'), ('4_INCREASING', 'Croissance')], default='3_FLAT', max_length=30, verbose_name='Expectations'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /crm/migrations/0014_auto_20201212_1430.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-12-12 14:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0013_auto_20200331_2307'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='client', 15 | name='vat_id', 16 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 17 | ), 18 | migrations.AddField( 19 | model_name='company', 20 | name='vat_id', 21 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 22 | ), 23 | migrations.AddField( 24 | model_name='subsidiary', 25 | name='vat_id', 26 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /crm/migrations/0015_client_billing_lang.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-12-13 14:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0014_auto_20201212_1430'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='client', 15 | name='billing_lang', 16 | field=models.CharField(choices=[('fr-fr', 'French'), ('en-en', 'English')], default='fr-fr', max_length=10, verbose_name='Language'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /crm/migrations/0016_auto_20201213_1508.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-12-13 15:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0015_client_billing_lang'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='client', 15 | name='billing_lang', 16 | field=models.CharField(choices=[('fr-fr', 'French'), ('en-en', 'English')], default='fr-fr', max_length=10, verbose_name='Billing language'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /crm/migrations/0017_auto_20221210_1715.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.16 on 2022-12-10 17:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0016_auto_20201213_1508'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='clientorganisation', 15 | name='legal_description', 16 | field=models.TextField(blank=True, null=True, verbose_name='Legal description'), 17 | ), 18 | migrations.AddField( 19 | model_name='clientorganisation', 20 | name='legal_id', 21 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 22 | ), 23 | migrations.AddField( 24 | model_name='clientorganisation', 25 | name='vat_id', 26 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 27 | ), 28 | migrations.AddField( 29 | model_name='company', 30 | name='legal_id', 31 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 32 | ), 33 | migrations.AddField( 34 | model_name='subsidiary', 35 | name='legal_id', 36 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='VAT id'), 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /crm/migrations/0018_auto_20221210_1725.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.16 on 2022-12-10 17:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0017_auto_20221210_1715'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='clientorganisation', 15 | name='legal_id', 16 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='Legal id'), 17 | ), 18 | migrations.AlterField( 19 | model_name='company', 20 | name='legal_id', 21 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='Legal id'), 22 | ), 23 | migrations.AlterField( 24 | model_name='subsidiary', 25 | name='legal_id', 26 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='Legal id'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /crm/migrations/0019_remove_client_vat_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.16 on 2022-12-10 17:30 2 | 3 | from django.db import migrations 4 | 5 | def forwards(apps, schema_editor): 6 | Client = apps.get_model("crm", "Client") 7 | for client in Client.objects.exclude(vat_id__isnull=True): 8 | client.organisation.vat_id = client.vat_id 9 | client.organisation.save() 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | ('crm', '0018_auto_20221210_1725'), 15 | ] 16 | 17 | operations = [ 18 | migrations.RunPython(forwards), 19 | migrations.RemoveField( 20 | model_name='client', 21 | name='vat_id', 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /crm/migrations/0023_auto_20230326_1749.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-26 17:49 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0022_auto_20221211_2156'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='BusinessSector', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=100, unique=True, verbose_name='Name')), 19 | ], 20 | ), 21 | migrations.AddField( 22 | model_name='clientorganisation', 23 | name='business_sector', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='crm.businesssector', verbose_name='Business sector'), 25 | ), 26 | migrations.AddField( 27 | model_name='company', 28 | name='business_sector', 29 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='crm.businesssector', verbose_name='Business sector'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /crm/migrations/0024_alter_businesssector_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-07 22:53 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('crm', '0023_auto_20230326_1749'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='businesssector', 15 | options={'verbose_name': 'Business sector', 'verbose_name_plural': 'Business sectors'}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /crm/migrations/0025_alter_company_businessowner.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-02-23 23:39 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0010_alter_consultant_telegram_id'), 11 | ('crm', '0024_alter_businesssector_options'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='company', 17 | name='businessOwner', 18 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_business_owner', to='people.consultant', verbose_name='Business owner'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /crm/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/crm/migrations/__init__.py -------------------------------------------------------------------------------- /data/documents/README: -------------------------------------------------------------------------------- 1 | This repository will contain documents, you can consider to add a webdav access to it 2 | Pydici will look at it to discover document related to: 3 | - commerce 4 | - devlivery 5 | - input 6 | 7 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | memcached: 4 | image: memcached 5 | command: memcached -l 0.0.0.0 -I 32m -m 128 6 | ports: 7 | - "11211:11211" 8 | redis: 9 | image: redis 10 | ports: 11 | - "6379:6379" 12 | mariadb: 13 | image: mariadb 14 | ports: 15 | - "3306:3306" 16 | environment: 17 | MARIADB_ROOT_PASSWORD: root 18 | MARIADB_DATABASE: pydici 19 | MARIADB_USER: pydici 20 | MARIADB_PASSWORD: pydici 21 | django: 22 | build: . 23 | image: digitalfox/pydici:latest 24 | command: python manage.py runserver_plus 0.0.0.0:8888 25 | volumes: 26 | - .:/code 27 | ports: 28 | - "8888:8888" 29 | environment: 30 | PYDICI_ENV: "dev" 31 | depends_on: 32 | - memcached 33 | - redis 34 | - mariadb 35 | celery: 36 | image: digitalfox/pydici:latest 37 | command: celery -A pydici worker -B -l INFO -Q pydici --concurrency=1 38 | volumes: 39 | - .:/code 40 | environment: 41 | PYDICI_ENV: "dev" 42 | depends_on: 43 | - memcached 44 | - redis 45 | - mariadb 46 | bot: 47 | image: digitalfox/pydici:latest 48 | environment: 49 | PYDICI_ENV: "dev" 50 | command: python bot/pydici_bot.py 51 | volumes: 52 | - .:/code 53 | depends_on: 54 | - django 55 | -------------------------------------------------------------------------------- /expense/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Expense management module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /expense/admin.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | """ 3 | Django administration setup 4 | @author: Sébastien Renard 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | 8 | from django.contrib import admin 9 | 10 | from expense.models import Expense, ExpenseCategory, ExpensePayment 11 | 12 | 13 | class ExpenseAdmin(admin.ModelAdmin): 14 | list_display = ("user", "description", "lead", "state", "chargeable", "creation_date", "update_date") 15 | ordering = ("-creation_date",) 16 | search_fields = ["description", "lead__name", "lead__client__organisation__company__name", "user__first_name", "user__last_name", "user__username"] 17 | list_filter = ["workflow_in_progress", "state", "chargeable", "corporate_card", "user"] 18 | actions = None 19 | 20 | 21 | class ExpenseCategoryAdmin(admin.ModelAdmin): 22 | list_display = ("name",) 23 | actions = None 24 | 25 | 26 | class ExpensePaymentAdmin(admin.ModelAdmin): 27 | list_display = ("id", "payment_date", "user", "amount") 28 | 29 | 30 | admin.site.register(Expense, ExpenseAdmin) 31 | admin.site.register(ExpenseCategory, ExpenseCategoryAdmin) 32 | admin.site.register(ExpensePayment, ExpensePaymentAdmin) 33 | -------------------------------------------------------------------------------- /expense/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/expense/management/__init__.py -------------------------------------------------------------------------------- /expense/migrations/0002_auto_20181120_1518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-20 15:18 3 | 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('expense', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='expense', 18 | name='expensePayment', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='expense.ExpensePayment'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /expense/migrations/0003_expense_state.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.17 on 2018-12-29 17:06 3 | 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('expense', '0002_auto_20181120_1518'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='expense', 17 | name='state', 18 | field=models.CharField(choices=[('REQUESTED', 'requested'), ('VALIDATED', 'validated'), ('REJECTED', 'rejected'), ('NEEDS_INFORMATION', 'needs information'), ('CONTROLLED', 'controlled'), ('PAID', 'paid')], default='REQUESTED', max_length=20, verbose_name='state'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /expense/migrations/0004_auto_20190113_2220.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-01-13 22:20 3 | 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('expense', '0003_expense_state'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='expense', 17 | name='state', 18 | field=models.CharField(choices=[('REQUESTED', 'Requested'), ('VALIDATED', 'Validated'), ('REJECTED', 'Rejected'), ('NEEDS_INFORMATION', 'Needs information'), ('CONTROLLED', 'Controlled'), ('PAID', 'Paid')], default='REQUESTED', max_length=20, verbose_name='state'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /expense/migrations/0005_auto_20190113_2250.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-01-13 22:50 3 | 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('expense', '0004_auto_20190113_2220'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='expense', 17 | name='creation_date', 18 | field=models.DateField(auto_now_add=True, verbose_name='Date'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /expense/migrations/0006_expense_vat.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-09-23 21:47 2 | 3 | from decimal import Decimal 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('expense', '0005_auto_20190113_2250'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='expense', 16 | name='vat', 17 | field=models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=7, verbose_name='VAT (€)'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /expense/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/expense/migrations/__init__.py -------------------------------------------------------------------------------- /i18n.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Extract messages from source code, update .po file and compile .mo file 3 | # Sebastien Renard (Sebastien.Renard@digitalfox.org) - april 2010 4 | 5 | OLD=$PWD 6 | cd $(dirname $0) 7 | python manage.py makemessages -a -e ".html,.txt,.py" -i data -i venv -i venv3 -i node_modules 8 | cd locale && python ../manage.py compilemessages 9 | cd $OLD 10 | -------------------------------------------------------------------------------- /leads/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Pydici Lead management module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /leads/migrations/0002_fix_description_for_markdown.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | from django.db.models import F, Func, Value 6 | 7 | def fix_list(apps, schema_editor): 8 | Lead = apps.get_model("leads", "Lead") 9 | # Use bulk update to avoid triggering default Lead.save, signals and date field auto update. 10 | Lead.objects.all().update(description=Func(F("description"), Value(": \n"), Value(":\n"), function="replace")) 11 | Lead.objects.all().update(description=Func(F("description"), Value(":\n-"), Value(":\n\n-"), function="replace")) 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [ 17 | ('leads', '0001_initial'), 18 | ] 19 | 20 | operations = [ 21 | migrations.RunPython(fix_list), 22 | ] 23 | -------------------------------------------------------------------------------- /leads/migrations/0003_lead_administrative_notes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('leads', '0002_fix_description_for_markdown'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='lead', 16 | name='administrative_notes', 17 | field=models.TextField(verbose_name='Administrative notes', blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /leads/migrations/0004_auto_20181120_1518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-20 15:18 3 | 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('leads', '0003_lead_administrative_notes'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='lead', 18 | name='business_broker', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lead_broker', to='crm.BusinessBroker', verbose_name='Business broker'), 20 | ), 21 | migrations.AlterField( 22 | model_name='lead', 23 | name='paying_authority', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lead_paying', to='crm.BusinessBroker', verbose_name='Paying authority'), 25 | ), 26 | migrations.AlterField( 27 | model_name='lead', 28 | name='responsible', 29 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lead_responsible', to='people.Consultant', verbose_name='Responsible'), 30 | ), 31 | migrations.AlterField( 32 | model_name='lead', 33 | name='salesman', 34 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='people.SalesMan', verbose_name='Salesman'), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /leads/migrations/0005_auto_20190819_1519.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.5 on 2019-08-19 15:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('leads', '0004_auto_20181120_1518'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='lead', 15 | name='staffing', 16 | field=models.ManyToManyField(blank=True, limit_choices_to={'active': True, 'productive': True}, to='people.Consultant'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /leads/migrations/0006_add_warmup_periodic_task.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from django.db import migrations, models 3 | from django.conf import settings 4 | 5 | from django_celery_beat.models import CrontabSchedule, PeriodicTask 6 | 7 | 8 | def add_default_tasks(apps, schema_editor): 9 | c, _ = CrontabSchedule.objects.get_or_create(minute=0, hour=6, day_of_week="*", 10 | day_of_month="*", month_of_year="*", 11 | timezone=settings.TIME_ZONE) 12 | t = PeriodicTask.objects.create(name="learning model warm up", task="leads.tasks.learning_warmup", crontab=c) 13 | 14 | 15 | def remove_default_tasks(apps, schema_editor): 16 | PeriodicTask.objects.filter(task="leads.tasks.learning_warmup").delete() 17 | 18 | 19 | class Migration(migrations.Migration): 20 | dependencies = [ 21 | ("leads", "0005_auto_20190819_1519"), 22 | ("core", "0012_create_default_periodic_tasks") 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(add_default_tasks, remove_default_tasks), 27 | ] 28 | -------------------------------------------------------------------------------- /leads/migrations/0007_alter_lead_business_broker_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-02-23 23:39 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0010_alter_consultant_telegram_id'), 11 | ('crm', '0025_alter_company_businessowner'), 12 | ('leads', '0006_add_warmup_periodic_task'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='lead', 18 | name='business_broker', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_broker', to='crm.businessbroker', verbose_name='Business broker'), 20 | ), 21 | migrations.AlterField( 22 | model_name='lead', 23 | name='paying_authority', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_paying', to='crm.businessbroker', verbose_name='Paying authority'), 25 | ), 26 | migrations.AlterField( 27 | model_name='lead', 28 | name='responsible', 29 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_responsible', to='people.consultant', verbose_name='Responsible'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /leads/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/leads/migrations/__init__.py -------------------------------------------------------------------------------- /locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, sys 3 | 4 | if __name__ == "__main__": 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pydici.settings") 6 | 7 | from django.core.management import execute_from_command_line 8 | 9 | execute_from_command_line(sys.argv) 10 | -------------------------------------------------------------------------------- /media/css/ajax_select.css: -------------------------------------------------------------------------------- 1 | .results_on_deck .ui-icon-trash { 2 | float: left; 3 | cursor: pointer; 4 | } 5 | .results_on_deck { 6 | display:inline-block; 7 | padding: 0.25em 0; 8 | } 9 | form .aligned .results_on_deck { 10 | padding-left: 38px; 11 | margin-left: 7em; 12 | } 13 | .results_on_deck > div { 14 | margin-bottom: 0.5em; 15 | } 16 | .ui-autocomplete-loading { 17 | background: url('../images/loading-indicator.gif') no-repeat; 18 | background-origin: content-box; 19 | background-position: right; 20 | } 21 | ul.ui-autocomplete { 22 | /* 23 | this is the dropdown menu. 24 | 25 | if max-width is not set and you are using django-admin 26 | then the dropdown is the width of your whole page body (totally wrong). 27 | 28 | this sets max-width at 60% which is graceful at full page or in a popup 29 | or on a small width window. 30 | 31 | fixed width is harder see http://stackoverflow.com/questions/4607164/changing-width-of-jquery-ui-autocomplete-widgets-individually 32 | */ 33 | max-width: 60%; 34 | 35 | margin: 0; 36 | padding: 0; 37 | position: absolute; 38 | } 39 | ul.ui-autocomplete li { 40 | list-style-type: none; 41 | padding: 0; 42 | } 43 | ul.ui-autocomplete li a { 44 | display: block; 45 | padding: 2px 3px; 46 | cursor: pointer; 47 | } 48 | -------------------------------------------------------------------------------- /media/css/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /media/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /media/css/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /media/css/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /media/css/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /media/css/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /media/css/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/css/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /media/css/jquery.autocomplete.css: -------------------------------------------------------------------------------- 1 | .ac_results { 2 | padding: 0px; 3 | border: 1px solid black; 4 | background-color: white; 5 | overflow: hidden; 6 | z-index: 99999; 7 | } 8 | 9 | .ac_results ul { 10 | width: 100%; 11 | list-style-position: outside; 12 | list-style: none; 13 | padding: 0; 14 | margin: 0; 15 | } 16 | 17 | .ac_results li { 18 | margin: 0px; 19 | padding: 2px 5px; 20 | cursor: default; 21 | display: block; 22 | /* 23 | if width will be 100% horizontal scrollbar will apear 24 | when scroll mode will be used 25 | */ 26 | /*width: 100%;*/ 27 | font: menu; 28 | font-size: 12px; 29 | /* 30 | it is very important, if line-height not setted or setted 31 | in relative units scroll will be broken in firefox 32 | */ 33 | line-height: 16px; 34 | overflow: hidden; 35 | } 36 | 37 | .ac_loading { 38 | background: white url('../img/ajax-loader.gif') right center no-repeat; 39 | } 40 | 41 | .ac_odd { 42 | background-color: #eee; 43 | } 44 | 45 | .ac_over { 46 | background-color: #0A246A; 47 | color: white; 48 | } 49 | -------------------------------------------------------------------------------- /media/css/pydici-pdf.css: -------------------------------------------------------------------------------- 1 | @page 2 | { 3 | size: A4; 4 | margin-left: 10mm; 5 | margin-right: 10mm; 6 | margin-top: 10mm; 7 | margin-bottom: 20mm; 8 | } 9 | 10 | body 11 | { 12 | font-size : 10px; 13 | } 14 | 15 | div.footer { 16 | display: block; 17 | text-align: center; 18 | font-size: 8px; 19 | width: 100%; 20 | position: fixed; 21 | bottom: -15mm; 22 | } 23 | 24 | .receipt { 25 | max-width: 100%; 26 | height: auto; 27 | max-height: 18cm; 28 | 29 | } 30 | 31 | /* used to reduce some default boostrap margin */ 32 | .small-margin { 33 | margin-bottom: 5px; 34 | } 35 | -------------------------------------------------------------------------------- /media/css/subtotal.min.css: -------------------------------------------------------------------------------- 1 | .pvtAxisLabel,.pvtRowLabel{font-family:Verdana}table.pvtTable .pvtRowLabel{vertical-align:top;white-space:nowrap}table.pvtTable .pvtAxisLabel,table.pvtTable .pvtColLabel{font-family:Verdana;white-space:nowrap}.pvtColSubtotal,.pvtRowSubtotal{background-color:#EFEFEF!important;font-weight:700}.expanded{cursor:pointer} 2 | -------------------------------------------------------------------------------- /media/fonts/circles/circles-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/fonts/circles/circles-webfont.eot -------------------------------------------------------------------------------- /media/fonts/circles/circles-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/fonts/circles/circles-webfont.ttf -------------------------------------------------------------------------------- /media/fonts/circles/circles-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/fonts/circles/circles-webfont.woff -------------------------------------------------------------------------------- /media/images/Sorting icons.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/Sorting icons.psd -------------------------------------------------------------------------------- /media/images/back_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/back_disabled.png -------------------------------------------------------------------------------- /media/images/back_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/back_enabled.png -------------------------------------------------------------------------------- /media/images/back_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/back_enabled_hover.png -------------------------------------------------------------------------------- /media/images/forward_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/forward_disabled.png -------------------------------------------------------------------------------- /media/images/forward_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/forward_enabled.png -------------------------------------------------------------------------------- /media/images/forward_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/forward_enabled_hover.png -------------------------------------------------------------------------------- /media/images/loading-indicator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/loading-indicator.gif -------------------------------------------------------------------------------- /media/images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/sort_asc.png -------------------------------------------------------------------------------- /media/images/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/sort_asc_disabled.png -------------------------------------------------------------------------------- /media/images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/sort_both.png -------------------------------------------------------------------------------- /media/images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/sort_desc.png -------------------------------------------------------------------------------- /media/images/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/images/sort_desc_disabled.png -------------------------------------------------------------------------------- /media/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/ajax-loader.gif -------------------------------------------------------------------------------- /media/img/changelist-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/changelist-bg.gif -------------------------------------------------------------------------------- /media/img/default-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/default-bg-reverse.gif -------------------------------------------------------------------------------- /media/img/default-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/default-bg.gif -------------------------------------------------------------------------------- /media/img/gis/move_vertex_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/gis/move_vertex_off.png -------------------------------------------------------------------------------- /media/img/gis/move_vertex_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/gis/move_vertex_on.png -------------------------------------------------------------------------------- /media/img/icon-no.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon-no.gif -------------------------------------------------------------------------------- /media/img/icon-unknown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon-unknown.gif -------------------------------------------------------------------------------- /media/img/icon-yes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon-yes.gif -------------------------------------------------------------------------------- /media/img/icon_addlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_addlink.gif -------------------------------------------------------------------------------- /media/img/icon_alert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_alert.gif -------------------------------------------------------------------------------- /media/img/icon_calendar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_calendar.gif -------------------------------------------------------------------------------- /media/img/icon_changelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_changelink.gif -------------------------------------------------------------------------------- /media/img/icon_clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_clock.gif -------------------------------------------------------------------------------- /media/img/icon_deletelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_deletelink.gif -------------------------------------------------------------------------------- /media/img/icon_error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_error.gif -------------------------------------------------------------------------------- /media/img/icon_success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/icon_success.gif -------------------------------------------------------------------------------- /media/img/nav-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/nav-bg-reverse.gif -------------------------------------------------------------------------------- /media/img/nav-bg-selected.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/nav-bg-selected.gif -------------------------------------------------------------------------------- /media/img/nav-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/nav-bg.gif -------------------------------------------------------------------------------- /media/img/sorting-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/sorting-icons.gif -------------------------------------------------------------------------------- /media/img/tool-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tool-left.gif -------------------------------------------------------------------------------- /media/img/tool-left_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tool-left_over.gif -------------------------------------------------------------------------------- /media/img/tool-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tool-right.gif -------------------------------------------------------------------------------- /media/img/tool-right_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tool-right_over.gif -------------------------------------------------------------------------------- /media/img/tooltag-add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tooltag-add.gif -------------------------------------------------------------------------------- /media/img/tooltag-add_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tooltag-add_over.gif -------------------------------------------------------------------------------- /media/img/tooltag-arrowright.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tooltag-arrowright.gif -------------------------------------------------------------------------------- /media/img/tooltag-arrowright_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/img/tooltag-arrowright_over.gif -------------------------------------------------------------------------------- /media/js/jquery.init.js: -------------------------------------------------------------------------------- 1 | // Puts the included jQuery into our own namespace 2 | var django = { 3 | "jQuery": jQuery.noConflict(true) 4 | }; 5 | -------------------------------------------------------------------------------- /media/js/select2_locale_fr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Select2 French translation 3 | */ 4 | (function ($) { 5 | "use strict"; 6 | $.fn.select2.locales = []; 7 | $.fn.select2.locales['fr'] = { 8 | formatMatches: function (matches) { return matches + " résultats sont disponibles, utilisez les flèches haut et bas pour naviguer."; }, 9 | formatNoMatches: function () { return "Aucun résultat trouvé"; }, 10 | formatInputTooShort: function (input, min) { var n = min - input.length; return "Saisissez " + n + " caractère" + (n == 1? "" : "s") + " supplémentaire" + (n == 1? "" : "s") ; }, 11 | formatInputTooLong: function (input, max) { var n = input.length - max; return "Supprimez " + n + " caractère" + (n == 1? "" : "s"); }, 12 | formatSelectionTooBig: function (limit) { return "Vous pouvez seulement sélectionner " + limit + " élément" + (limit == 1 ? "" : "s"); }, 13 | formatLoadMore: function (pageNumber) { return "Chargement de résultats supplémentaires…"; }, 14 | formatSearching: function () { return "Recherche en cours…"; } 15 | }; 16 | 17 | $.extend($.fn.select2.defaults, $.fn.select2.locales['fr']); 18 | })(jQuery); 19 | -------------------------------------------------------------------------------- /media/pydici/README: -------------------------------------------------------------------------------- 1 | The following icons are kindly taken from the Oxygen icon set of the KDE project: 2 | -timesheet.png 3 | -staffing.png 4 | -configure.png 5 | 6 | More info on http://www.oxygen-icons.org/ -------------------------------------------------------------------------------- /media/pydici/company_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/company_logo.png -------------------------------------------------------------------------------- /media/pydici/configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/configure.png -------------------------------------------------------------------------------- /media/pydici/decrease.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/decrease.png -------------------------------------------------------------------------------- /media/pydici/fd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/fd.png -------------------------------------------------------------------------------- /media/pydici/fg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/fg.png -------------------------------------------------------------------------------- /media/pydici/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/header.png -------------------------------------------------------------------------------- /media/pydici/increase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/increase.png -------------------------------------------------------------------------------- /media/pydici/lead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/lead.png -------------------------------------------------------------------------------- /media/pydici/menu/corner_inset_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/menu/corner_inset_left.png -------------------------------------------------------------------------------- /media/pydici/menu/corner_inset_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/menu/corner_inset_right.png -------------------------------------------------------------------------------- /media/pydici/menu/corner_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/menu/corner_left.png -------------------------------------------------------------------------------- /media/pydici/menu/corner_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/menu/corner_right.png -------------------------------------------------------------------------------- /media/pydici/menu/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/menu/dot.png -------------------------------------------------------------------------------- /media/pydici/menu/magnifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/menu/magnifier.png -------------------------------------------------------------------------------- /media/pydici/receipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/pydici/receipt.png -------------------------------------------------------------------------------- /media/tables2/img/arrow-active-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/arrow-active-down.png -------------------------------------------------------------------------------- /media/tables2/img/arrow-active-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/arrow-active-up.png -------------------------------------------------------------------------------- /media/tables2/img/arrow-inactive-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/arrow-inactive-down.png -------------------------------------------------------------------------------- /media/tables2/img/arrow-inactive-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/arrow-inactive-up.png -------------------------------------------------------------------------------- /media/tables2/img/false.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/false.gif -------------------------------------------------------------------------------- /media/tables2/img/header-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/header-bg.png -------------------------------------------------------------------------------- /media/tables2/img/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/missing.png -------------------------------------------------------------------------------- /media/tables2/img/pagination-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/pagination-bg.gif -------------------------------------------------------------------------------- /media/tables2/img/true.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/media/tables2/img/true.gif -------------------------------------------------------------------------------- /people/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | People management module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /people/migrations/0002_rename_rateobjective_daily_rate_field_to_rate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RenameField( 15 | model_name='rateobjective', 16 | old_name="daily_rate", 17 | new_name='rate', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /people/migrations/0003_add_objective_rate_type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0002_rename_rateobjective_daily_rate_field_to_rate'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='rateobjective', 16 | name='rate_type', 17 | field=models.CharField(default='DAILY_RATE', max_length=30, verbose_name='Rate type', choices=[('DAILY_RATE', 'daily rate'), ('PROD_RATE', 'production rate')]), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /people/migrations/0004_auto_20170817_2015.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0003_add_objective_rate_type'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='rateobjective', 16 | name='rate', 17 | field=models.IntegerField(null=True, verbose_name='Rate'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /people/migrations/0005_auto_20181120_1518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-20 15:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('people', '0004_auto_20170817_2015'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='consultant', 18 | name='manager', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_as_manager', to='people.Consultant'), 20 | ), 21 | migrations.AlterField( 22 | model_name='consultant', 23 | name='staffing_manager', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_as_staffing_manager', to='people.Consultant'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /people/migrations/0006_auto_20190310_2209.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.18 on 2019-03-10 22:09 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('people', '0005_auto_20181120_1518'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='consultant', 18 | name='subcontractor_company', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='crm.Supplier'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /people/migrations/0007_consultant_telegram_alias.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-01-16 18:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0006_auto_20190310_2209'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='consultant', 15 | name='telegram_alias', 16 | field=models.CharField(blank=True, max_length=50, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0008_consultant_telegram_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-01-30 22:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0007_consultant_telegram_alias'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='consultant', 15 | name='telegram_id', 16 | field=models.IntegerField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0009_compute_consultants_tasks_periodic_task.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from django.db import migrations, models 3 | from django.conf import settings 4 | 5 | from django_celery_beat.models import CrontabSchedule, PeriodicTask 6 | 7 | 8 | def add_tasks(apps, schema_editor): 9 | c, _ = CrontabSchedule.objects.get_or_create(minute=0, hour=6, day_of_week="*", 10 | day_of_month="*", month_of_year="*", 11 | timezone=settings.TIME_ZONE) 12 | t = PeriodicTask.objects.create(name="compute all consultants tasks", task="people.tasks.compute_all_consultants_tasks", crontab=c) 13 | 14 | 15 | def remove_tasks(apps, schema_editor): 16 | PeriodicTask.objects.filter(task="people.tasks.compute_all_consultants_tasks").delete() 17 | 18 | 19 | class Migration(migrations.Migration): 20 | dependencies = [ 21 | ("people", "0008_consultant_telegram_id"), 22 | ("core", "0012_create_default_periodic_tasks") 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(add_tasks, remove_tasks), 27 | ] 28 | -------------------------------------------------------------------------------- /people/migrations/0010_alter_consultant_telegram_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-07 14:23 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0009_compute_consultants_tasks_periodic_task'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='consultant', 15 | name='telegram_id', 16 | field=models.BigIntegerField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/people/migrations/__init__.py -------------------------------------------------------------------------------- /people/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """URL dispatcher for people module 3 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 4 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 5 | """ 6 | 7 | from django.urls import re_path 8 | import people.views as v 9 | import people.api as a 10 | 11 | people_urls = [ 12 | re_path(r'^home/consultant/(?P\d+)/$', v.consultant_home_by_id, name="consultant_home_by_id"), 13 | re_path(r'^home/consultant/(?P[a-zA-Z]{3})/$', v.consultant_home, name="consultant_home"), 14 | re_path(r'^detail/consultant/(?P\d+)/$', v.consultant_detail, name="consultant_detail"), 15 | re_path(r'^achievements/consultant/(?P\d+)/$', v.consultant_achievements, name="consultant_achievements"), 16 | re_path(r'^detail/subcontractor/(?P\d+)/$', v.subcontractor_detail, name="subcontractor_detail"), 17 | re_path(r'^tasks/consultants_tasks', v.consultants_tasks, name="consultants_tasks"), 18 | re_path(r'^api/consultant_list$', a.consultant_list, name="consultant_list"), 19 | re_path(r'^api/consultant_provisioning$', a.consultant_provisioning, name="consultant_provisioning"), 20 | re_path(r'^api/consultant_deactivation$', a.consultant_deactivation, name="consultant_deactivation"), 21 | re_path(r'^graph/people-count/?$', v.graph_people_count, name="graph_people_count"), 22 | ] 23 | -------------------------------------------------------------------------------- /pydici/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 4 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 5 | """ 6 | 7 | from .pydici_celery import app as celery_app 8 | 9 | __all__ = ('celery_app',) 10 | -------------------------------------------------------------------------------- /pydici/pydici_celery.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Celery initialisation 5 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 6 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 7 | """ 8 | 9 | import os 10 | 11 | from django.core.mail import mail_admins 12 | from celery import Celery 13 | from celery.signals import task_failure 14 | 15 | # Set the default Django settings module for the 'celery' program. 16 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pydici.settings") 17 | 18 | app = Celery("pydici") 19 | app.config_from_object("django.conf:settings", namespace='CELERY') 20 | 21 | # Load task modules from all registered Django apps. 22 | app.autodiscover_tasks() 23 | 24 | 25 | @task_failure.connect() 26 | def email_on_task_crash(**kwargs): 27 | """Simple mail alert on task crash""" 28 | subject = "Pydici task error in {sender.name}".format(**kwargs).replace("\n", " ") 29 | message = "{sender.name} was called with {args} / {kwargs} and failed with error message:\n\n{einfo}".format(**kwargs) 30 | mail_admins(subject, message) 31 | -------------------------------------------------------------------------------- /pydici/settings/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 4 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 5 | """ 6 | 7 | import os 8 | 9 | from split_settings.tools import optional, include 10 | 11 | env = os.environ.get("PYDICI_ENV", "prod") 12 | 13 | if env not in ("dev", "prod"): 14 | print("You need to set PYDICI_ENV environment variable to 'dev' or 'prod'") 15 | exit() 16 | 17 | include("pydici.py", "django.py", f"{env}.py", optional("local.py")) 18 | 19 | 20 | try: 21 | SECRET_KEY 22 | except NameError as e: 23 | print(f"{e}. You need to declare it in settings in your settings/local.py file") 24 | exit() 25 | -------------------------------------------------------------------------------- /pydici/settings/dev.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Development specific settings. 4 | Don't customise this file, use local.py for specific local settings 5 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 6 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 7 | """ 8 | import sys 9 | from django.conf import settings 10 | 11 | DEBUG = True 12 | 13 | SECRET_KEY = "very-very-secret-dev-key" 14 | 15 | MIDDLEWARE = [ 16 | 'django.middleware.common.CommonMiddleware', 17 | 'django.contrib.sessions.middleware.SessionMiddleware', 18 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 19 | 'django.contrib.messages.middleware.MessageMiddleware', 20 | 'core.middleware.ScopeMiddleware', 21 | 'userswitch.middleware.UserSwitchMiddleware', 22 | 'auditlog.middleware.AuditlogMiddleware', 23 | ] 24 | 25 | if settings.DEBUG and "test" not in sys.argv: 26 | TEMPLATES[0]["OPTIONS"]["debug"] = True 27 | INSTALLED_APPS.extend(['debug_toolbar']) 28 | MIDDLEWARE.extend(['debug_toolbar.middleware.DebugToolbarMiddleware',]) 29 | 30 | 31 | ALLOWED_HOSTS = ("localhost",) 32 | 33 | DEBUG_TOOLBAR_CONFIG = { # use settings.DEBUG instead of DEBUG to allow django test runner to force settings.DEBUG to False 34 | 'SHOW_TOOLBAR_CALLBACK': lambda request: True if settings.DEBUG else False 35 | } 36 | 37 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 38 | 39 | CRISPY_FAIL_SILENTLY = False 40 | 41 | # Use to allow LiveServerTestCase to work in serialized_rollback mode in order to preserve migration data during tests 42 | TEST_NON_SERIALIZED_APPS = ['django.contrib.contenttypes', 43 | 'django.contrib.auth'] 44 | -------------------------------------------------------------------------------- /pydici/settings/prod.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Production specific settings. 4 | Don't customise this file, use local.py for specific local settings 5 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 6 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 7 | """ 8 | DEBUG = False 9 | 10 | MIDDLEWARE = [ 11 | 'django.middleware.common.CommonMiddleware', 12 | 'django.contrib.sessions.middleware.SessionMiddleware', 13 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 14 | 'django.contrib.messages.middleware.MessageMiddleware', 15 | 'django.contrib.auth.middleware.RemoteUserMiddleware', 16 | 'auditlog.middleware.AuditlogMiddleware', 17 | 'core.middleware.ScopeMiddleware', 18 | ] 19 | 20 | 21 | AUTHENTICATION_BACKENDS = [ 22 | 'django.contrib.auth.backends.RemoteUserBackend', 23 | ] 24 | 25 | MEDIA_ROOT = PYDICI_ROOTDIR -------------------------------------------------------------------------------- /pydici/wsgi.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """ 3 | WSGI Wrapper for production deployment 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | 8 | import os 9 | 10 | import sys 11 | 12 | pydici_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), os.path.pardir) 13 | if pydici_path not in sys.path: 14 | sys.path.append(pydici_path) 15 | sys.path.append(os.path.join(pydici_path,"pydici")) 16 | 17 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pydici.settings") 18 | 19 | # This application object is used by the development server 20 | # as well as any WSGI server configured to use this file. 21 | from django.core.wsgi import get_wsgi_application 22 | application = get_wsgi_application() 23 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | django-debug-toolbar==4.4.6 2 | Werkzeug==3.0.3 3 | ipython==8.26.0 4 | git+https://github.com//harpb/django-userswitch#egg=django-userswitch 5 | selenium==4.23 6 | webdriver-manager==4.0.2 7 | -------------------------------------------------------------------------------- /requirements-sklearn.txt: -------------------------------------------------------------------------------- 1 | scikit-learn==1.6.1 2 | numpy==2.1 3 | scipy==1.14.1 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django>=4.2.2,<5.0.0 2 | django-extensions==3.2.3 3 | django_tables2==2.6.0 4 | django-taggit==4.0 5 | django-taggit-templatetags2==1.6.1 6 | django-crispy-forms==2.3 7 | crispy-bootstrap5==2024.10 8 | Django-Select2==8.4.1 9 | pymemcache==4.0 10 | django-datatables-view==1.20.0 11 | WeasyPrint==62.3 12 | django-weasyprint==2.3.0 13 | bleach==5.0.1 14 | Markdown==3.4.3 15 | pypdf==3.17.4 16 | ortools>=9.11.0,<9.12.0 17 | celery[redis]==5.2.7 18 | django-split-settings==1.1.0 19 | django-celery-results==2.4.0 20 | django-celery-beat==2.5.0 21 | mysqlclient==2.1.1 22 | python-telegram-bot[job-queue]==20.1 23 | django-auditlog==2.3.0 24 | factur-x==3.6 25 | django-countries==7.5.1 26 | factory_boy==3.3 27 | -------------------------------------------------------------------------------- /run-dev-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | killall memcached 3 | /usr/sbin/memcached -l 127.0.0.1 -d -I 32m -m 128 4 | /usr/sbin/redis-server & 5 | python -Wd manage.py runserver_plus 8888 $* 6 | killall memcached 7 | killall redis-server 8 | -------------------------------------------------------------------------------- /run-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd $(dirname $0) 4 | 5 | if [ -z "$RUNNING_IN_GH" ]; 6 | then 7 | echo "running test in local" 8 | else 9 | echo "inside tests in GH" 10 | sleep 10 11 | fi 12 | 13 | python manage.py test $* 14 | -------------------------------------------------------------------------------- /scripts/check_signed_pdf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | """ 5 | Try to guess if commerce documents are present and signed 6 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 7 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 8 | """ 9 | 10 | import os 11 | import csv 12 | from pypdf import PdfReader 13 | from pypdf.errors import PdfReadError 14 | 15 | csv_writter = csv.writer(open("check_signed_pdf_result.csv", "w"), dialect="excel") 16 | csv_writter.writerow(["lead", "pdf files", "Office pdf files", "pdf types", "diag"]) 17 | 18 | for dirpath, dirnames, filenames in os.walk("."): 19 | if not dirpath.endswith("/commerce"): 20 | continue 21 | lead = dirpath.split("/")[-2] 22 | print(f"======= {lead} =======") 23 | pdf = [] 24 | for filename in filenames: 25 | if not filename.endswith(".pdf"): 26 | continue 27 | try: 28 | reader = PdfReader(os.path.join(dirpath, filename)) 29 | pdf.append(reader.metadata.creator or "unknown") 30 | except PdfReadError: 31 | pdf.append("unreadable pdf") 32 | except AttributeError: 33 | pdf.append("unreadable file") 34 | pdf_count = len(pdf) 35 | microsoft_pdf_count = len([i for i in pdf if "Microsoft" in i]) 36 | if pdf_count < 2: 37 | msg = f"{lead} has only {pdf_count} PDF files in commerce dir" 38 | elif pdf_count > 1 and microsoft_pdf_count == pdf_count: 39 | msg = f"{lead} only have Ms Office PDF in commerce dir. Signed PDF may be missing" 40 | else: 41 | msg = "Looks good" 42 | print(pdf) 43 | print(msg) 44 | csv_writter.writerow([lead, pdf_count, microsoft_pdf_count, "|".join(pdf), msg]) -------------------------------------------------------------------------------- /scripts/export_users.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | """ 5 | Export users in simple csv format 6 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 7 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 8 | """ 9 | 10 | import csv 11 | import sys 12 | import os 13 | from os.path import abspath, join, pardir, dirname 14 | 15 | 16 | # # Setup django envt & django imports 17 | PYDICI_DIR = abspath(join(dirname(__file__), pardir)) 18 | os.environ['DJANGO_SETTINGS_MODULE'] = "pydici.settings" 19 | 20 | sys.path.append(PYDICI_DIR) # Add project path to python path 21 | 22 | # Ensure we are in the good current working directory (pydici home) 23 | os.chdir(PYDICI_DIR) 24 | 25 | # Init and model loading 26 | from django.core.wsgi import get_wsgi_application 27 | application = get_wsgi_application() 28 | 29 | # Pydici imports 30 | from people.models import Consultant 31 | 32 | output = csv.writer(open("users.csv", "w")) 33 | 34 | for c in Consultant.objects.filter(active=True, subcontractor=False): 35 | u = c.get_user() 36 | line = ([c.trigramme.lower(), 37 | *c.name.split(" ", 1), 38 | u.email, 39 | c.company.name.lower().replace(" ", "_"), 40 | ]) 41 | output.writerow(line) 42 | 43 | 44 | -------------------------------------------------------------------------------- /scripts/import_objectives.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | """ 5 | Import objectives and rates from CSV format 6 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 7 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 8 | """ 9 | 10 | import csv 11 | from datetime import datetime 12 | import sys 13 | import os 14 | from os.path import abspath, join, pardir, dirname 15 | 16 | 17 | # # Setup django envt & django imports 18 | PYDICI_DIR = abspath(join(dirname(__file__), pardir)) 19 | os.environ['DJANGO_SETTINGS_MODULE'] = "pydici.settings" 20 | 21 | sys.path.append(PYDICI_DIR) # Add project path to python path 22 | 23 | # Ensure we are in the good current working directory (pydici home) 24 | os.chdir(PYDICI_DIR) 25 | 26 | # Init and model loading 27 | from django.core.wsgi import get_wsgi_application 28 | application = get_wsgi_application() 29 | 30 | # Pydici imports 31 | from people.models import Consultant, RateObjective 32 | 33 | objectives = csv.reader(open(sys.argv[1], "r")) 34 | 35 | for line in objectives: 36 | if not line[0] or line[0]=="Consultant": 37 | continue 38 | try: 39 | c = Consultant.objects.get(trigramme=line[0]) 40 | except: 41 | print(f"Warning, no consultant for {line[0]}") 42 | continue 43 | start = datetime.strptime(line[21], "%Y-%m-%d") 44 | prod_rate = float(line[10].strip("%")) 45 | daily_rate = int(line[7]) 46 | RateObjective.objects.get_or_create(consultant=c, start_date=start, rate=daily_rate, rate_type="DAILY_RATE") 47 | RateObjective.objects.get_or_create(consultant=c, start_date=start, rate=prod_rate, rate_type="PROD_RATE") 48 | 49 | -------------------------------------------------------------------------------- /staffing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Staffing management module 4 | @author: Sébastien Renard (sebastien.renard@digitalfox.org) 5 | @license: AGPL v3 or newer (http://www.gnu.org/licenses/agpl-3.0.html) 6 | """ 7 | -------------------------------------------------------------------------------- /staffing/migrations/0002_auto_20160214_1402.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staffing', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='mission', 16 | name='probability', 17 | field=models.IntegerField(default=50, verbose_name='Proba'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /staffing/migrations/0003_auto_20160228_1841.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staffing', '0002_auto_20160214_1402'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='lunchticket', 16 | name='no_ticket', 17 | field=models.BooleanField(default=True, verbose_name='No lunch ticket'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /staffing/migrations/0004_auto_20181120_1518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-20 15:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('staffing', '0003_auto_20160228_1841'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='mission', 18 | name='responsible', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mission_responsible', to='people.Consultant', verbose_name='Responsible'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /staffing/migrations/0005_auto_20190819_1519.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.5 on 2019-08-19 15:19 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staffing', '0004_auto_20181120_1518'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='financialcondition', 16 | name='mission', 17 | field=models.ForeignKey(limit_choices_to={'active': True}, on_delete=django.db.models.deletion.CASCADE, to='staffing.Mission'), 18 | ), 19 | migrations.AlterField( 20 | model_name='staffing', 21 | name='mission', 22 | field=models.ForeignKey(limit_choices_to={'active': True}, on_delete=django.db.models.deletion.CASCADE, to='staffing.Mission'), 23 | ), 24 | migrations.AlterField( 25 | model_name='timesheet', 26 | name='mission', 27 | field=models.ForeignKey(limit_choices_to={'active': True}, on_delete=django.db.models.deletion.CASCADE, to='staffing.Mission'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /staffing/migrations/0006_auto_20191206_1734.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-12-06 17:34 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staffing', '0005_auto_20190819_1519'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='AnalyticCode', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('code', models.CharField(max_length=100)), 19 | ('description', models.CharField(blank=True, max_length=100, null=True, verbose_name='Description')), 20 | ], 21 | ), 22 | migrations.AddField( 23 | model_name='mission', 24 | name='analytic_code', 25 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='staffing.AnalyticCode'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /staffing/migrations/0007_auto_20191206_1752.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-12-06 17:52 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staffing', '0006_auto_20191206_1734'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='mission', 16 | name='analytic_code', 17 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='staffing.AnalyticCode', verbose_name='analytic code'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /staffing/migrations/0008_auto_20191206_2006.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-12-06 20:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0007_auto_20191206_1752'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='analyticcode', 15 | name='code', 16 | field=models.CharField(max_length=100, unique=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0009_auto_20200227_1731.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2020-02-27 17:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0008_auto_20191206_2006'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='mission', 15 | name='end_date', 16 | field=models.DateField(blank=True, null=True, verbose_name='End date'), 17 | ), 18 | migrations.AddField( 19 | model_name='mission', 20 | name='start_date', 21 | field=models.DateField(blank=True, null=True, verbose_name='Start date'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /staffing/migrations/0010_mission_management_mode.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2020-03-01 13:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0009_auto_20200227_1731'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='mission', 15 | name='management_mode', 16 | field=models.CharField(blank=True, choices=[('LIMITED', 'Limited'), ('ELASTIC', 'Elastic')], max_length=30, null=True, verbose_name='Management mode'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0011_auto_20200302_1457.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2020-03-02 14:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0010_mission_management_mode'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='mission', 15 | name='management_mode', 16 | field=models.CharField(choices=[('LIMITED', 'Limité'), ('ELASTIC', 'Élastique'), ('NONE', 'None')], default='NONE', max_length=30, verbose_name='Management mode'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0012_auto_20210116_1825.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-01-16 18:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0011_auto_20200302_1457'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='mission', 15 | name='management_mode', 16 | field=models.CharField(choices=[('LIMITED', 'Limité'), ('ELASTIC', 'Élastique'), ('NONE', 'Aucun')], default='NONE', max_length=30, verbose_name='Management mode'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0013_auto_20220826_1752.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.15 on 2022-08-26 17:52 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('crm', '0016_auto_20201213_1508'), 11 | ('staffing', '0012_auto_20210116_1825'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='MarketingProduct', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('code', models.CharField(max_length=100, unique=True)), 20 | ('description', models.CharField(blank=True, max_length=100, null=True, verbose_name='Description')), 21 | ('active', models.BooleanField(default=True, verbose_name='Active')), 22 | ('subsidiary', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crm.subsidiary', verbose_name='Subsidiary')), 23 | ], 24 | options={ 25 | 'unique_together': {('code', 'subsidiary')}, 26 | }, 27 | ), 28 | migrations.AddField( 29 | model_name='mission', 30 | name='marketing_product', 31 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='staffing.marketingproduct', verbose_name='marketing product'), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /staffing/migrations/0014_mission_min_charge_per_day.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.15 on 2022-09-16 20:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0013_auto_20220826_1752'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='mission', 15 | name='min_charge_per_day', 16 | field=models.FloatField(default=0, verbose_name='Min charge per day'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0015_alter_mission_management_mode.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.16 on 2023-02-02 09:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0014_mission_min_charge_per_day'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='mission', 15 | name='management_mode', 16 | field=models.CharField(choices=[('LIMITED', 'Limité'), ('LIMITED_INDIVIDUAL', 'Limited individual'), ('ELASTIC', 'Élastique'), ('NONE', 'Aucun')], default='NONE', max_length=30, verbose_name='Management mode'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0016_auto_20230303_1646.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-03 16:46 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0015_alter_mission_management_mode'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='mission', 15 | name='management_mode', 16 | field=models.CharField(choices=[('LIMITED', 'Limité'), ('LIMITED_INDIVIDUAL', 'Limité individuel'), ('ELASTIC', 'Élastique'), ('NONE', 'Aucun')], default='NONE', max_length=30, verbose_name='Management mode'), 17 | ), 18 | migrations.AlterField( 19 | model_name='timesheet', 20 | name='working_date', 21 | field=models.DateField(db_index=True, verbose_name='Date'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /staffing/migrations/0017_alter_staffing_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-07 14:23 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0016_auto_20230303_1646'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='staffing', 15 | options={'ordering': ['staffing_date', 'consultant', '-mission__nature', 'mission__lead__client__organisation__company__name', 'mission__description', 'id'], 'verbose_name': 'Staffing'}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /staffing/migrations/0017_auto_20230307_0000.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-07 00:00 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0016_auto_20230303_1646'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='staffing', 15 | options={'ordering': ['staffing_date', 'consultant', '-mission__nature', 'mission__lead__client__organisation__company__name', 'mission__description', 'id'], 'verbose_name': 'Staffing'}, 16 | ), 17 | migrations.AlterField( 18 | model_name='staffing', 19 | name='staffing_date', 20 | field=models.DateField(db_index=True, verbose_name='Date'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /staffing/migrations/0018_merge_20230310_1735.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-03-10 17:35 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0017_alter_staffing_options'), 10 | ('staffing', '0017_auto_20230307_0000'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /staffing/migrations/0019_auto_20230607_2253.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-07 22:53 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0018_merge_20230310_1735'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='mission', 15 | old_name='min_charge_per_day', 16 | new_name='min_charge_multiple_per_day', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0020_alter_mission_min_charge_multiple_per_day.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-18 21:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0019_auto_20230607_2253'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='mission', 15 | name='min_charge_multiple_per_day', 16 | field=models.FloatField(default=0, verbose_name='Min charge multiple per day'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0021_mission_client_deal_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-27 22:39 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0020_alter_mission_min_charge_multiple_per_day'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='mission', 15 | name='client_deal_id', 16 | field=models.CharField(blank=True, max_length=100, verbose_name='Client deal id'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0022_alter_mission_responsible.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-02-24 10:18 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0010_alter_consultant_telegram_id'), 11 | ('staffing', '0021_mission_client_deal_id'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='mission', 17 | name='responsible', 18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_responsible', to='people.consultant', verbose_name='Responsible'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /staffing/migrations/0023_mission_always_displayed.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-03-23 20:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0022_alter_mission_responsible'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='mission', 15 | name='always_displayed', 16 | field=models.BooleanField(default=False, verbose_name='Always displayed'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staffing/migrations/0024_staffing_staffing_st_mission_2c479a_idx.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.16 on 2025-04-21 18:46 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staffing', '0023_mission_always_displayed'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddIndex( 14 | model_name='staffing', 15 | index=models.Index(fields=['mission', 'consultant'], name='staffing_st_mission_2c479a_idx'), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /staffing/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalfox/pydici/a9235da6088d60f0ab39d5364928067b4ccc339d/staffing/migrations/__init__.py -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {% load i18n %} 3 | 4 | {% block stylesheet %}{{ MEDIA_URL }}css/dashboard.css{% endblock %} 5 | 6 | {% block bodyclass %}dashboard{% endblock %} 7 | 8 | {% block breadcrumbs %}{% endblock %} 9 | 10 | {% block content %} 11 |
12 | {% blocktrans %} 13 |

Sorry, page {{ request_path }} does not exist.

14 |

It could have been deleted or may have never existed.

15 |

If you think that this could be a mistake, please, contact an administrator 16 | and explain the condition that lead to that error.

17 | {% endblocktrans %} 18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {% load i18n %} 3 | 4 | {% block stylesheet %}{{ MEDIA_URL }}css/dashboard.css{% endblock %} 5 | 6 | {% block bodyclass %}dashboard{% endblock %} 7 | 8 | {% block breadcrumbs %}{% endblock %} 9 | 10 | {% block content %} 11 |
12 | {% blocktrans %} 13 |

Sorry, this is an application error

14 |

Administrators have been warned by email of this error

15 |

Don't hesitate to contact them to explain conditions that lead 16 | to that error in order to help help diagnotics.

17 | {% endblocktrans %} 18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /templates/admin/base_site.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base.html" %} 2 | 3 | {% block title %}{{ title }}{% endblock %} 4 | 5 | {% block branding %} 6 |

Pydici

7 | {% endblock %} 8 | 9 | {% block nav-global %}{% endblock %} 10 | -------------------------------------------------------------------------------- /templates/batch/timesheet_warning_email.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% autoescape off %}{% blocktrans %} 2 | Hello {{ consultant }}, 3 | 4 | This is an automatic email from the pydici software application. 5 | {% endblocktrans %}{% if days %}{% blocktrans with month|date:"F Y" as month %} 6 | Your timesheet for the {{ days }} days of {{ month }} is not correct:{% endblocktrans %}{% else %}{% blocktrans with month|date:"F Y" as month %} 7 | Your timesheet for {{ month }} is not correct:{% endblocktrans %}{% endif %} 8 | {% if incomplete_days %}{% blocktrans %}- days incomplete: {{ incomplete_days }}{% endblocktrans %}{% endif %} 9 | {% if surbooking_days %}{% blocktrans %}- days in surbooking: {{ surbooking_days }}{% endblocktrans %}{% endif %} 10 | {% blocktrans %} 11 | Please correct your timesheet quickly at the following address: 12 | {{ url }} 13 | 14 | Regards 15 | {% endblocktrans %} 16 | {% endautoescape %} -------------------------------------------------------------------------------- /templates/billing/client_bills_archive.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "All client bills" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
{% trans "Bill id" %}{% trans "Subsidiary" %}{% trans "Deal id" %}{% trans "Lead" %}{% trans "Creation date" %}{% trans "Due date" %}{% trans "Payment date" %}{% trans "State" %}{% trans "Amount (€ excl tax)" %}{% trans "Amount (€ incl tax)" %}{% trans "Comments" %}{% trans "File" %}
29 | 30 | 31 | {% with "clientbills_table" as table_id %} 32 | {% include "core/_datatables.html" %} 33 | {% endwith %} 34 | 35 | {% endblock %} -------------------------------------------------------------------------------- /templates/billing/client_bills_in_creation.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "Client bills in creation" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
{% trans "Bill id" %}{% trans "Subsidiary" %}{% trans "Lead" %}{% trans "Responsibles" %}{% trans "Creation date" %}{% trans "State" %}{% trans "Amount (€ excl tax)" %}{% trans "Amount (€ incl tax)" %}{% trans "Comments" %}
26 | 27 | 28 | {% with "clientbills_table" as table_id %} 29 | {% include "core/_datatables.html" %} 30 | {% endwith %} 31 | 32 | {% endblock %} -------------------------------------------------------------------------------- /templates/billing/graph_billing.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display cilent bill graph to be included with a jquery load #} 2 | {# One must include in its extrajs block core/_billboard.html to load according js/css #} {% load i18n %} 3 | 4 | {% if graph_data %} 5 |

{% trans "Client billing history" %}

6 |
7 | 40 | {% endif %} -------------------------------------------------------------------------------- /templates/billing/graph_outstanding_billing.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display outstanding billing graph to be included with a jquery load #} 2 | {# One must include in its extrajs block core/_c3.htm to load according js/css #} 3 | {% load i18n %} 4 | 5 | 6 | {% if graph_data %} 7 |

{% trans "Outstanding billing" %}

8 | 9 |
10 | 11 | 43 | {% else %} 44 | 47 | {% endif %} -------------------------------------------------------------------------------- /templates/billing/graph_yearly_billing.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display yearly turnover graph to be included with a jquery load #} 2 | {# One must include in its extrajs block core/_billboard.html to load according js/css #} {% load i18n %} 3 | 4 |
5 | 6 | 7 | -------------------------------------------------------------------------------- /templates/billing/supplier_bill_form.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block title %}{{ title }}{% endblock %} 7 | 8 | {% block content %} 9 | 10 | 11 |
12 | {% csrf_token %} 13 |

{% trans "Supplier bill" %}

14 | {% crispy bill_form %} 15 |
16 | {% if can_delete %} 17 | 20 | {% endif %} 21 | 22 |
23 |
24 | 25 | {% include "core/_datepicker.html" %} 26 | {% endblock %} -------------------------------------------------------------------------------- /templates/billing/supplier_bills_archive.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "All supplier bills" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
{% trans "Bill id" %}{% trans "Supplier" %}{% trans "Subsidiary" %}{% trans "Lead" %}{% trans "Creation date" %}{% trans "Due date" %}{% trans "Payment date" %}{% trans "State" %}{% trans "Amount (€ excl tax)" %}{% trans "Amount (€ incl tax)" %}{% trans "Comments" %}{% trans "File" %}
29 | 30 | 31 | {% with "clientbills_table" as table_id %} 32 | {% include "core/_datatables.html" %} 33 | {% endwith %} 34 | 35 | {% endblock %} -------------------------------------------------------------------------------- /templates/core/_access_forbidden.html: -------------------------------------------------------------------------------- 1 | {# Page fragment included in standard fordiden page #} 2 | {# and in fordiden ajax load pages #} 3 | {% load i18n %} 4 | 5 | {% blocktrans %} 6 |

Sorry, this page is not available.

7 |

You profil does not allow you to access to this page.

8 |

If you think that this could be a mistake, please, contact an administrator 9 | to update your profile:

10 | {% endblocktrans %} 11 |
    12 | {% for admin in admins %} 13 |
  • {{ admin.0 }}
  • 14 | {% endfor %} 15 |
-------------------------------------------------------------------------------- /templates/core/_ajax_popup_post.html: -------------------------------------------------------------------------------- 1 | {# JS Fragment to submit a popup form with Ajax POST #} 2 | {# Form id must be {{ formid }} (use {% with... %}) #} 3 | {# Popover class must be {{ popup_class }} (use {% with... %}) #} 4 | {# Target field for data ass must be {{ target_id }} (use {% with... %}) #} 5 | 6 | -------------------------------------------------------------------------------- /templates/core/_ajax_post.html: -------------------------------------------------------------------------------- 1 | {# JS Fragment to submit form with Ajax POST #} 2 | {# Form id must be {{ formid }} (use {% with... %}) #} 3 | {# Content placeholder id must {{ formcontent }} (use {% with... %}) #} 4 | 5 | -------------------------------------------------------------------------------- /templates/core/_billboard.html: -------------------------------------------------------------------------------- 1 | {# load billboard script and css #} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /templates/core/_datatables-dj-tables.html: -------------------------------------------------------------------------------- 1 | {# Standard pydici datatables definition #} 2 | {# context: table_id #} 3 | {# context: datatable_options #} 4 | 5 | {% load i18n %} 6 | 7 | 8 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /templates/core/_datatables.html: -------------------------------------------------------------------------------- 1 | {# Standard pydici datatables definition #} 2 | {# context: table_id, datatable_options, data_url #} 3 | 4 | {% load i18n %} 5 | 6 | -------------------------------------------------------------------------------- /templates/core/_date_column.html: -------------------------------------------------------------------------------- 1 | {# context : date #} 2 | 3 | {{ date|date:"j M y"|default_if_none:"-" }} -------------------------------------------------------------------------------- /templates/core/_datepicker.html: -------------------------------------------------------------------------------- 1 | {# Add a bootstrap date pickup to every input with datepickup css class #} 2 | {% load l10n %} 3 | {% load i18n %} 4 | 5 | 26 | -------------------------------------------------------------------------------- /templates/core/_form.html: -------------------------------------------------------------------------------- 1 | {# Standard pydici single form fragment for generic views #} 2 | {% load crispy_forms_tags %} 3 | 4 | {% crispy form form.helper %} 5 | {% include "core/_datepicker.html" %} -------------------------------------------------------------------------------- /templates/core/_messages.html: -------------------------------------------------------------------------------- 1 | {# Display django admin message with boostrap style #} 2 | 3 | {% for message in messages %} 4 |
{{ message }}
5 | {% endfor %} 6 | 7 | -------------------------------------------------------------------------------- /templates/core/_select2.html: -------------------------------------------------------------------------------- 1 | {# Load select2 with appropriate parameters #} 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /templates/core/delete.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {# Standard pydici delete confirmation for generic views #} 3 | 4 | {% load i18n %} 5 | 6 | {% block title %}{% blocktrans %}Deletion of {{ object }}{% endblocktrans %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 |
{% csrf_token %} 11 | {% blocktrans %} 12 | Do you really want to delete "{{ object }}" ? 13 | {% endblocktrans %} 14 |

15 | 16 | 19 |
20 | {% endblock %} -------------------------------------------------------------------------------- /templates/core/forbidden.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {% load i18n %} 3 | {% load static %} 4 | 5 | {% block content %} 6 | 7 |
8 | {% include "core/_access_forbidden.html" %} 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /templates/core/form.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {# Standard pydici form for generic views #} 3 | 4 | {% load i18n %} 5 | {% load crispy_forms_tags %} 6 | 7 | {% block title %}{{ title }}{% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% crispy form form.helper %} 12 | 13 | {% include "core/_datepicker.html" %} 14 | 15 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/__leads_state_donut.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {# display lead states as a donut #} 3 | {# context : "data" in compatible billboard.js format and "title" of donut #} 4 | 5 |
6 |
7 | {% if data %} 8 | 34 | {% endif %} 35 |
-------------------------------------------------------------------------------- /templates/crm/_client_picto.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display client picto according to its expectations and business alignment #} 2 | {# Context : client #} 3 | {% load i18n %} 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /templates/crm/_clientcompany_list.html: -------------------------------------------------------------------------------- 1 | {# Fragment of page to be included to display client companies list and pie graph #} 2 | {# companies must be {{ companies }} #} 3 | 4 | {% load i18n %} 5 | 6 |

{% trans "All companies list" %}

7 | {% regroup companies by business_sector as business_sector_list %} 8 | {% for business_sector in business_sector_list %} 9 |

{{ business_sector.grouper|default_if_none:_("uncategorized") }}

10 |
    11 | {% for company in business_sector.list %} 12 | {% with company_number=business_sector.list|length %} 13 | {% with first_letter=company.name|slice:"1"|upper %} 14 | {% if company_number > 50 %} 15 | {% ifchanged first_letter %} 16 |
17 |

{{ first_letter }}

18 |
    19 | {% endifchanged %} 20 | {% endif %} 21 | {% endwith %} 22 | {% endwith %} 23 |
  • 24 | {{ company }} 25 |
  • 26 | {% endfor %} 27 |
28 | {% endfor %} 29 | -------------------------------------------------------------------------------- /templates/crm/_contact_item.html: -------------------------------------------------------------------------------- 1 | {# This template fragments can be included to display contact item in a table #} 2 | {# Context : contact object #} 3 | 4 | {{ contact.name }} 5 | {{ contact.companies }} 6 | {{ contact.function }} 7 | {{ contact.email|urlize }} 8 | {{ contact.phone }} 9 | {{ contact.mobile_phone }} 10 | {{ contact.fax }} 11 | -------------------------------------------------------------------------------- /templates/crm/_contact_list.html: -------------------------------------------------------------------------------- 1 | {# This template fragments can be included to display contact list #} 2 | {# Context : contacts as a list a contact #} 3 | 4 | {% load i18n %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for contact in contacts %} 20 | {% include "crm/_contact_item.html" %} 21 | {% endfor %} 22 | 23 |
{% trans "name" %}{% trans "company" %}{% trans "function" %}{% trans "email" %}{% trans "phone" %}{% trans "mobile phone" %}{% trans "fax" %}
24 | 25 | -------------------------------------------------------------------------------- /templates/crm/_mission_contact_form.html: -------------------------------------------------------------------------------- 1 | {# form to create a new contact linked to a mission #} 2 | {# context: missionContactForm and contactForm #} 3 | {% load crispy_forms_tags %} 4 | {% load i18n %} 5 | 6 |
9 | {% crispy missionContactForm missionContactForm.inline_helper %} 10 | {% crispy contactForm contactForm.inline_helper %} 11 | {% crispy companyForm companyForm.inline_helper %} 12 | 13 |
-------------------------------------------------------------------------------- /templates/crm/businessbroker_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "Business broker list" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

{% trans "Business brokers" %}

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
{% trans "company" %}{% trans "contact" %}{% trans "Name used for billing" %}
22 | 23 | 24 | {% with "business_broker_table" as table_id %} 25 | {% include "core/_datatables.html" %} 26 | {% endwith %} 27 | 28 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/client-popup.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load crispy_forms_tags %} 3 | 4 | 5 |
6 | {% crispy clientForm clientForm.inline_helper %} 7 | {% crispy contactForm contactForm.inline_helper %} 8 | {% crispy organisationForm organisationForm.inline_helper %} 9 | {% crispy companyForm companyForm.inline_helper %} 10 | 11 | 12 | 13 |
14 | 15 | 16 | {% with "pydici-ajax-form-client" as formid %} 17 | {% with "clientModal" as modal_id %} 18 | {% with "id_client" as target_id %} 19 | {% include "core/_ajax_popup_post.html" %} 20 | {% endwith %} 21 | {% endwith %} 22 | {% endwith %} 23 | 24 | -------------------------------------------------------------------------------- /templates/crm/client.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block title %}{% trans "Add or modify a Client" %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 | {% if client %} 11 | 18 | {% endif %} 19 | 20 | {% crispy form form.helper %} 21 | 22 | {% include "core/_datepicker.html" %} 23 | 24 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/client_organisation.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block title %}{% trans "Add or modify a Client Organisation" %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 | {% if client_organisation %} 11 | 17 | {% endif %} 18 | 19 | {% crispy form form.helper %} 20 | 21 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/clientcompany.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block title %}{% trans "Add or modify a company" %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 | {% crispy form form.helper %} 11 | 12 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/clientcompany_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block bodyclass %}dashboard{% endblock %} 6 | 7 | {% block breadcrumbs %}{% endblock %} 8 | 9 | {% block title %}{% trans "All companies list" %}{% endblock %} 10 | 11 | {% block content %} 12 | 13 |
14 | 15 | {% include "crm/_clientcompany_list.html" %} 16 | 17 |
18 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/clientcompany_ranking.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load l10n %} 5 | 6 | {% block title %}{% trans "Client ranking" %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% for line in data %} 25 | 26 | 27 | 28 | {% endfor %} 29 | 30 |
{% trans "Client" %}{% trans "Business sector" %}{% trans "Alignment" %}{% trans "Expectations" %}{% trans "Daily rate rank" %}{% trans "Daily rate" %}{% trans "Sales (k€)" %}{% trans "Last year sales (k€)" %}
{{ line|join:"" }}
31 | 32 | {% with "client_ranking" as table_id %}{% include "core/_datatables-dj-tables.html" %}{% endwith %} 33 | 34 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/contact_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "Contacts adressebook" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
{% trans "name" %}{% trans "company" %}{% trans "function" %}{% trans "email" %}{% trans "phone" %}{% trans "mobile phone" %}{% trans "contacts" %}
24 | 25 | 26 | {% with "contacts_table" as table_id %} 27 | {% include "core/_datatables.html" %} 28 | {% endwith %} 29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /templates/crm/supplier_list.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "Suppliers list" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

{% trans "Suppliers" %}

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
{% trans "company" %}{% trans "contact" %}
20 | 21 | 22 | {% with "supplier_table" as table_id %} 23 | {% include "core/_datatables.html" %} 24 | {% endwith %} 25 | 26 | {% endblock %} -------------------------------------------------------------------------------- /templates/expense/_expense_chargeable.html: -------------------------------------------------------------------------------- 1 | {# fragment to display if an expense if chargeable, charged or not #} 2 | {# context : expense object #} 3 | {% load i18n %} 4 | 5 | {% if expense.chargeable %} 6 | {% if expense.clientbill_set.all %} 7 | {% for bill in expense.clientbill_set.all %} 8 | {{ bill }} 9 | 10 | {% endfor %} 11 | {% elif expense.billexpense_set.all %} 12 | {% for billexpense in expense.billexpense_set.all %} 13 | {{ billexpense.bill }} 14 | 15 | {% endfor %} 16 | {% else %} 17 | {% trans "Yes, but not charged yet" %} 18 | {% endif %} 19 | {% else %} 20 | No 21 | {% endif %} -------------------------------------------------------------------------------- /templates/expense/_expense_receipt_modal.html: -------------------------------------------------------------------------------- 1 | {# Modal popover to display expense receipt #} 2 | 3 | 4 | 17 | 18 | 19 | 20 | 37 | -------------------------------------------------------------------------------- /templates/expense/_expense_state_column.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load l10n %} 3 | {# context : record (expense object) #} 4 |
5 | {% if record.state == "PAID" %} 6 | {% trans "Paid" %} 7 | {% else %} 8 | {{ record.get_state_display }} 9 | {% endif %} 10 |
-------------------------------------------------------------------------------- /templates/expense/_expense_transitions_column.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load l10n %} 3 | {# context : record (expense object) #} 4 | 5 | {# out of band swap for expense state #} 6 | 7 | 8 | 9 |
10 | {% for transition, transition_label, transition_short_label in transitions %} 11 | 14 | {{ transition_short_label }} 15 | 16 | {% endfor %} 17 | 18 | {% if expense_edit_perm %} 19 | 20 | {% trans "Ed" %} 21 | 22 | 23 | {% trans "De" %} 24 | 25 | {% endif %} 26 | 27 | 28 | {% trans "Cl" %} 29 | 30 |
-------------------------------------------------------------------------------- /templates/expense/_expense_vat_column.html: -------------------------------------------------------------------------------- 1 | {# display VAT as htmx editable field #} 2 | {# context: expense object #} 3 | {% load l10n %} 4 | 10 | {{ expense.vat }} 11 | 12 | -------------------------------------------------------------------------------- /templates/expense/_receipt_column.html: -------------------------------------------------------------------------------- 1 | {# context : record (expense object) #} 2 | 3 | {% if record.receipt %} 4 | 5 | 6 | 7 | {% endif %} -------------------------------------------------------------------------------- /templates/expense/chargeable_expenses.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "Chargeable expenses review" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

{% trans "Non billed chargeable expenses" %}

10 | {% include "expense/expense_list.html" %} 11 | 12 | {% endblock %} -------------------------------------------------------------------------------- /templates/expense/expense_payment_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load pydici_filters %} 5 | {% load render_table from django_tables2 %} 6 | 7 | {% block extrastyle %} 8 | 9 | 10 | {% endblock %} 11 | 12 | {% block title %}{% trans "Expense payment detail" %}{% endblock %} 13 | 14 | {% block content %} 15 | 16 | {% if expense_table.data %} 17 |

{% trans "Expenses of payment n°" %}{{ expense_payment.id }} ({{ expense_payment.payment_date }})

18 | {% render_table expense_table %} 19 | {% with "expense_table" as table_id %}{% include "core/_datatables-dj-tables.html" %}{% endwith %} 20 | 21 |


22 |

{% trans "Total: " %} {{ expense_payment.amount }} €

23 | {% endif %} 24 | 25 | 26 | 27 | {% endblock %} -------------------------------------------------------------------------------- /templates/leads/IA_stats.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {% load i18n %} 3 | 4 | {% block breadcrumbs %}{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 | {% for year, salesmen in data.items %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% for salesman, months in salesmen.items %} 18 | 19 | 20 | {% for month, stat in months.items %} 21 | 28 | {% endfor %} 29 | 30 | {% endfor %} 31 |
Leads gagnés / perdus par provenance en {{ year }}
ProvenanceJanvierFévrierMarsAvrilMaiJuinJuilletAoûtSeptembreOctobreNovembreDécembre
{{ salesman|default:"Aucun" }} 22 | {% for state, value in stat.items %} 23 | {% if value != 0 %} 24 | {{ state }} : {{ value }}
25 | {% endif %} 26 | {% endfor %} 27 |
32 |

33 | {% endfor %} 34 |

Légende : G=>Gagné, P=>Perdu, A=>Abandonné

35 |
36 |
37 | {% endblock %} 38 | 39 | -------------------------------------------------------------------------------- /templates/leads/__lead_name.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load l10n %} 3 | {% load cache %} 4 | {# context: lead #} 5 | {% cache 60 lead_name lead.id %} 6 | {{ lead.name }} 7 | {% if not lead.checkBusinessDoc %} 8 | 9 | {% endif %} 10 | {% for mission in lead.mission_set.all %} 11 | {% if mission.no_more_staffing_since %} 12 | 13 | {% endif %} 14 | {% if not mission.defined_rates %} 15 | 16 | {% endif %} 17 | {% endfor %} 18 | 19 | {% endcache %} 20 | -------------------------------------------------------------------------------- /templates/leads/_lead_checkdoc.html: -------------------------------------------------------------------------------- 1 | {# This template fragment can be included to display lead doc check banner #} 2 | {# Context/Argument: Lead #} 3 | 4 | {% load i18n %} 5 | 6 | {% if not lead.checkBusinessDoc %} 7 |
8 |
{% trans "Business documents are missing. Please put them here: " %}{{ lead.getDocURL|urlize }}
9 |
10 | {% endif %} 11 | 12 | {% if not lead.checkDeliveryDoc %} 13 |
14 |
{% trans "Delivery documents are missing. Please put them here: " %}{{ lead.getDocURL|urlize }}
15 |
16 | {% endif %} -------------------------------------------------------------------------------- /templates/leads/_state_column.html: -------------------------------------------------------------------------------- 1 | {# Lead proba display #} 2 | {# context: lead.getStateProbal #} 3 | 4 | 5 | {% if proba %} 6 |
7 | 12 | {{proba.0.2}} % 13 |
14 | {% endif %} 15 | -------------------------------------------------------------------------------- /templates/leads/_tag_cloud.html: -------------------------------------------------------------------------------- 1 | {# This template fragment can be included to display lead tag cloud #} 2 | {# Context/Argument: None #} 3 | 4 | {% load i18n %} 5 | {% load taggit_templatetags2_tags %} 6 | {% load cache %} 7 | 8 | {% cache 3600 tag_cloud %} 9 |
10 |

{% trans "All lead tags" %}

11 | 12 | {% get_tagcloud as tags for 'leads.lead' %} 13 | 14 | 23 |
24 | {% endcache %} -------------------------------------------------------------------------------- /templates/leads/feed_title.txt: -------------------------------------------------------------------------------- 1 | {% autoescape off %}{{ obj.client.organisation.company }}/{{ obj.client.organisation.name }} : {{ obj.name }}{% endautoescape %} -------------------------------------------------------------------------------- /templates/leads/graph_won_rate.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display won rate graph to be included with a jquery load #} 2 | {# One must include in its extrajs block core/_billboard.html to load according js/css #} {% load i18n %} 3 | 4 | {% if graph_data %} 5 |
6 | 45 | {% endif %} -------------------------------------------------------------------------------- /templates/leads/lead.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block title %}{% trans "Add or modify a Lead" %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 |
11 | {% crispy form form.helper %} 12 |
13 | 14 | 15 | 28 | 29 | 30 | 46 | 47 | {% include "core/_datepicker.html" %} 48 | 49 | {% endblock %} -------------------------------------------------------------------------------- /templates/leads/lead_documents.html: -------------------------------------------------------------------------------- 1 | {# Ajax fragment to display lead documents #} 2 | {% load i18n %} 3 | 4 | {% for directory, docs in documents %} 5 |

{{ directory }}

6 |
    7 | {% for doc, url in docs %} 8 |
  • {{ doc }}
  • 9 | {% endfor %} 10 |
11 | {% endfor %} -------------------------------------------------------------------------------- /templates/leads/lead_mail.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {{ obj.client.organisation }} - {{ obj.name }} ({{ obj.deal_id }})
3 | 4 | {% trans "Contact: " %}{{ obj.client.contact }} {% if lead.client.contact.function %} ({{ lead.client.contact.function }}) {% endif %}
5 | 6 | {% trans "Description: " %}{{ obj.description|linebreaksbr }}
7 | 8 | {% trans "Responsible: " %} {% if obj.responsible %} {{ obj.responsible }} {% else %} {% trans "To be defined" %} {% endif %}
9 | 10 | {% trans "Salesman: " %}{% if obj.salesman %} {{ obj.salesman }} {% else %} {% trans "None" %} {% endif %}
11 | 12 | {% trans "Status: " %}{{ obj.get_state_display }} {% if obj.due_date %}(=> {{ obj.due_date }}){% endif %} / {% trans "Starting date" %} : {% if obj.start_date %} {{ obj.start_date }} {% else %} {% trans "To be defined" %} {% endif %}
13 | 14 | {% trans "Sales (kEuros): " %}{% if obj.sales %} {{ obj.sales }} {% else %} {% trans "Unknown" %} {% endif %}
15 | 16 | {% trans "Potential resource(s): " %}{{ obj.staffing_list }}
17 | 18 | {% trans "Action: " %}{% if obj.action %}{{ obj.action }}{% else %}{% trans "Nothing" %}{% endif %}
19 | 20 | 21 | {% with object=obj %} 22 | {% include "core/_object_history.html" %} 23 | {% endwith %} 24 | 25 |
{% trans "Change this lead" %} -------------------------------------------------------------------------------- /templates/leads/lead_mail.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %} 3 | {{ obj.client.organisation }} - {{ obj.name }} ({{ obj.deal_id }}) 4 | 5 | {% trans "Contact: " %}{{ obj.client.contact }} {% if lead.client.contact.function %} ({{ lead.client.contact.function }}) {% endif %} 6 | 7 | {% trans "Description: " %}{{ obj.description }} 8 | 9 | {% trans "Responsible: " %}{% if obj.responsible %} {{ obj.responsible }} {% else %} {% trans "To be defined" %}{% endif %} 10 | 11 | {% trans "Salesman: " %}{% if obj.salesman %} {{ obj.salesman }} {% else %} {% trans "None" %} {% endif %} 12 | 13 | {% trans "Status: " %}{{ obj.get_state_display }} {% if obj.due_date %}(=> {{ obj.due_date }}){% endif %} / {% trans "Start date: " %} {% if obj.start_date %} {{ obj.start_date }} {% else %} {% trans "To be defined" %} {% endif %} 14 | 15 | {% trans "Sales (kEuros): " %}{% if obj.sales %} {{ obj.sales }} {% else %} {% trans "Unknown" %} {% endif %} 16 | 17 | {% trans "Potential resource(s): " %}{{ obj.staffing_list }} 18 | 19 | {% trans "Action: " %}{% if obj.action %}{{ obj.action }}{% else %}{% trans "None" %}{% endif %} 20 | 21 | 22 | {% trans "Change this lead: " %}{{ lead_url }} 23 | {% endautoescape %} -------------------------------------------------------------------------------- /templates/leads/leads.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %} 6 | {% trans "Leads" %} 7 | {% endblock %} 8 | 9 | {% block content %} 10 |
11 |

{% trans "All leads" %}

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
{% trans "client" %}{% trans "name" %}{% trans "deal id" %}{% trans "subsidiary" %}{% trans "responsible" %}{% trans "amount (k€)" %}{% trans "status" %}{% trans "creation date" %}
28 |
29 | 30 | {% with "lead_table" as table_id %} 31 | {% include "core/_datatables.html" %} 32 | {% endwith %} 33 | {% endblock %} -------------------------------------------------------------------------------- /templates/leads/leads_to_bill.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %} 6 | {% trans "Leads still to be billed" %} 7 | {% endblock %} 8 | 9 | {% block content %} 10 |

{% trans "Leads still to be billed" %}

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
{% trans "client" %}{% trans "name" %}{% trans "deal id" %}{% trans "subsidiary" %}{% trans "responsible" %}{% trans "creation date" %}{% trans "amount (k€)" %}{% trans "Still to be billed (k€)" %}
27 | 28 | {% with "leads_to_bill_table" as table_id %} 29 | {% include "core/_datatables.html" %} 30 | {% endwith %} 31 | {% endblock %} -------------------------------------------------------------------------------- /templates/leads/mail.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% for leads in lead_group %} 3 |
    4 | {% for lead in leads %} 5 | {% ifchanged %}

    {{ lead.get_state_display }}

    {% endifchanged %} 6 |
  • {{ lead.client }} - {{ lead.name }} (maj : {{ lead.update_date_strf }})
  • 7 |
      8 |
    • {% trans "Due date: " %}{{ lead.due_date|default_if_none:_("To be defined")}} / {% trans "Start date: " %}{{ lead.start_date|default_if_none:_("To be defined") }} / {% if lead.sales %}{% trans "Sales: " %}{{ lead.sales }} kEuros{% else %}{% trans "Unknown" %}{% endif %}
    • 9 |
    • {% trans "Responsible: " %}{{ lead.responsible|default_if_none:_("None") }} / {% trans "Salesman: " %}{{ lead.salesman|default_if_none:_("None") }}
    • 10 |
    • {% trans "Staffing: " %}{{ lead.staffing_list }}
    • 11 |
    • {% trans "Action: " %}{{ lead.action|default_if_none:_("None") }}
    • 12 |

    13 | {% endfor %} 14 |
15 | {% endfor %} -------------------------------------------------------------------------------- /templates/leads/mail.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %}{% for leads in lead_group %} 3 | {% for lead in leads %} 4 | {% ifchanged %}__{{ lead.get_state_display }}__ 5 | {% endifchanged %} 6 | * {{ lead.client }} - {{ lead.name }} ({% trans "updated: " %}{{ lead.update_date_strf }}) 7 | o {% trans "Due date: " %}{{ lead.due_date|default_if_none:_("To be defined") }} / {% trans "Start date: " %}{{ lead.start_date|default_if_none:_("To be defined") }} 8 | o {% trans "Responsible: " %}{{ lead.responsible|default_if_none:_("To be defined") }} / {% trans "Salesman: " %}{{ lead.salesman|default_if_none:_("To be defined") }} / {% if lead.sales %}{% trans "Sales: " %}{{ lead.sales }} kEuros{% else %}{% trans "Unknown" %}{% endif %} 9 | o {% trans "Staffing: " %} {{ lead.staffing_list }} 10 | o {% trans "Action: " %}{{ lead.action|default_if_none:_("Nothing") }}{% endfor %}{% endfor %}{% endautoescape %} 11 | -------------------------------------------------------------------------------- /templates/leads/manage_tags.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "Tag management" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 | 11 |
12 |
13 |

{% trans "All tags" %}

14 |
15 | 16 | {% trans "Merge selected tags" %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
#{% trans "Name" %}
27 | {% with "tag_table" as table_id %} 28 | {% include "core/_datatables.html" %} 29 | {% endwith %} 30 |
31 |
32 |
33 | 34 | 44 | 45 | 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /templates/leads/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %}{% trans "All lead tags" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 | 11 |

{% blocktrans %}Leads for tag {{ tag }}{% endblocktrans %}

12 | 13 |
    14 | {% for lead in leads %} 15 |
  • 16 | {{ lead }} 17 |
  • 18 | {% endfor %} 19 |
20 | 21 | {% include "leads/_tag_cloud.html" %} 22 | 23 |
24 | {% endblock %} -------------------------------------------------------------------------------- /templates/people/graph_people_count.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display people count graph to be included with a jquery load #} 2 | {% load i18n %} 3 | 4 | {% if graph_data %} 5 |
6 | 46 | {% endif %} 47 | -------------------------------------------------------------------------------- /templates/people/subcontractor_detail.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if not consultant.active %} 3 |

{% trans "This consultant does not work anymore for the company" %}

4 | {% endif %} 5 | 6 | 7 |
8 |

{% trans "Contact" %}

9 |

{% if consultant.manager %}{% if consultant.manager != consultant %}{{ consultant.manager.full_name }}{% endif %}{% endif %}

10 |
11 | 12 |
13 |

{% trans "Clients" %}

14 |
    15 | {% for company in companies %} 16 |
  • {{ company }}
  • 17 | {% endfor %} 18 |
19 |
20 | 21 |
22 |

{% trans "Current missions" %}

23 |
    24 | {% for mission in missions %} 25 |
  • {{ mission }}
  • 26 | {% endfor %} 27 |
28 |
29 | 30 |
31 | {% if leads_as_staffee %} 32 |

{% trans "Current leads as resource" %}

33 |
    34 | {% for lead in leads_as_staffee %} 35 |
  • {{ lead }}
  • 36 | {% endfor %} 37 |
38 | {% endif %} 39 |
-------------------------------------------------------------------------------- /templates/staffing/_check_mission_billing_mode.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if not mission.billing_mode %}
{% trans "Please defined billing mode on description tab" %}
{% endif %} -------------------------------------------------------------------------------- /templates/staffing/_check_mission_margin.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% with remaining=mission.target_remaining %} 3 | {% if remaining < 0 and mission.management_mode != "ELASTIC" %} 4 |
5 | {% if mission.management_mode == "LIMITED" %} 6 |
7 | {% else %} 8 |
9 | {% endif %} 10 | 11 | {% trans "Current forecast exceeds mission price" %} ({{ remaining|floatformat:-2 }} k€) 12 |
13 |
14 | {% endif %} 15 | {% endwith %} 16 | -------------------------------------------------------------------------------- /templates/staffing/_check_mission_rates.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if not mission.defined_rates %} 3 |
4 |
5 | {% trans "Consultants rates are not fully defined. Define that on consultant tabs" %} 6 |
7 |
8 | {% endif %} -------------------------------------------------------------------------------- /templates/staffing/_check_prod_mission.html: -------------------------------------------------------------------------------- 1 | {# all controls of productive mission #} 2 | {% if mission.lead %} 3 | {% with mission.lead as lead %}{% include "leads/_lead_checkdoc.html" %}{% endwith %} 4 | {% include "staffing/_check_mission_billing_mode.html" %} 5 | {% include "staffing/_check_mission_rates.html" %} 6 | {% include "staffing/_check_mission_margin.html" %} 7 | 8 | 9 | {% endif %} -------------------------------------------------------------------------------- /templates/staffing/_consultant_prod_tooltip.html: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% trans "production rate" %}: {{ prod_rate|floatformat:-1 }} % ({% trans "objective" %} : {{ prod_rate_obj|floatformat:-1 }} %) / {{ prod_rate_delta|stringformat:"+d" }} € 2 | {% trans "daily rate" %}: {{ daily_rate|floatformat:0 }} € ({% trans "objective" %} : {{ daily_rate_obj|floatformat:0 }} €) / {{ daily_rate_delta|stringformat:"+d" }} € -------------------------------------------------------------------------------- /templates/staffing/_mission_consultants_rate.html: -------------------------------------------------------------------------------- 1 | {# fragment to display and allow inline edit of consultant rate for a mission #} 2 | {# context: consultant, rate #} 3 | {% load l10n %} 4 | {% load i18n %} 5 | 6 | 7 | {% include "people/__consultant_name.html" %} 8 | {% if edit %} 9 | 11 | 12 | {% if consultant.subcontractor %}{% endif %} 14 | 15 | {% else %} 16 | {{ rate.0.0 }} 17 | 18 | {% if consultant.subcontractor %}{{ rate.0.1 }}{% endif %} 19 | {% endif %} 20 | {% with o_rates=rate.1 %} 21 | {% for date, o_rate in o_rates %} 22 | {{ o_rate|default_if_none:"-" }} 23 | {% endfor %} 24 | {% endwith %} 25 | 26 | -------------------------------------------------------------------------------- /templates/staffing/_mission_table_archive_column.html: -------------------------------------------------------------------------------- 1 | {# This template fragment can be used to render the archive column of a mission table #} 2 | {# Context/Argument: a row of a mission #} 3 | 4 | {% load i18n %} 5 | {% load l10n %} 6 | {% if row.active %} 7 | 13 | {% else %} 14 | {{ row.archived_date|date:"j F Y" }} 15 | {% endif %} -------------------------------------------------------------------------------- /templates/staffing/_staffing_fold.html: -------------------------------------------------------------------------------- 1 | {# js that make staffing stanza foldable #} 2 | 3 | 19 | -------------------------------------------------------------------------------- /templates/staffing/feed_staffing_content.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "Mission: " %}{{ obj.mission }}
3 | {% trans "Consultant: " %} {{ obj.consultant }}
4 | {% trans "Staffing date: " %} {{ obj.staffing_date|date:"F Y" }}
5 | {% trans "Charge: " %} {{ obj.charge }}
6 | {% if obj.comment %} 7 | {% trans "Comment: " %} {{ obj.comment }}
8 | {% endif %} 9 | {% if obj.last_user %} 10 |
11 | {% trans "Updated by " %} {{ obj.last_user }} ({{ obj.update_date }}) 12 | {% endif %} 13 |
14 | 15 | 16 |
17 | {% trans "Change this consultant staffing" %} 18 |
19 | {% trans "Change this mission staffing" %} 20 |
21 | -------------------------------------------------------------------------------- /templates/staffing/feed_staffing_title.txt: -------------------------------------------------------------------------------- 1 | {% autoescape off %}{{ obj.mission }}/{{ obj.consultant }} ({{ obj.staffing_date }}){% endautoescape %} -------------------------------------------------------------------------------- /templates/staffing/graph_profile_rates.html: -------------------------------------------------------------------------------- 1 | {# Fragment that display timesheet and rates graph to be included with a jquery load #} 2 | {# One must include in its extrajs block core/_billboard.html to load according js/css #} {% load i18n %} 3 | 4 | {% if graph_data %} 5 |
6 | 37 | {% endif %} -------------------------------------------------------------------------------- /templates/staffing/mass_staffing.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | {% load i18n %} 3 | {% load crispy_forms_tags %} 4 | 5 | {% block title %}{% trans "Massive staffing" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% crispy form form.helper %} 10 | 11 | {% endblock %} -------------------------------------------------------------------------------- /templates/staffing/mission_consultants.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load l10n %} 3 | {# Fragment of page to be included in a mission_home tab #} 4 | 5 |

{% trans "Consultants currently implicated in this mission" %}

6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for date in objective_dates %} 14 | 15 | {% endfor %} 16 | 17 | 18 | 19 | {% for consultant, rate in rates.items %} 20 | {% include "staffing/_mission_consultants_rate.html" %} 21 | {% endfor %} 22 | 23 |
{% trans "Consultant" %}{% trans "Daily rate (€)" %}{% trans "Buy rate (€)" %}{% blocktranslate with d=date|date:"M Y" %}Objective {{ d }} (€){% endblocktranslate %}
24 |
25 | 26 | 29 | -------------------------------------------------------------------------------- /templates/staffing/missions.html: -------------------------------------------------------------------------------- 1 | {% extends "core/pydici.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block title %} 6 | {% trans "Missions" %} 7 | {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% if all %} 12 |

{% trans "All missions" %}

13 |
{% trans "Only display active missions" %}

14 | {% else %} 15 |

{% trans "Active missions" %}

16 |
{% trans "Also display inactive missions" %}

17 | {% endif %} 18 | 19 | {% include "staffing/_mission_table.html" %} 20 | 21 | {% endblock %} -------------------------------------------------------------------------------- /templates/staffing/pdc_detail.html: -------------------------------------------------------------------------------- 1 | {# html fragment displayed for pdc detail staffing #} 2 | {% load i18n %} 3 | {% load l10n %} 4 | 5 | {% if staffings %} 6 | 7 | 8 | 9 | 10 | 11 | {% for staffing in staffings %} 12 | 13 | 14 | 15 | {% endfor %} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
{% trans "Mission" %}{% trans "Charge" %}
{{ staffing.mission }}{{ staffing.charge }}
{% trans "total" %}{{ total }}
{% trans "available" %}{{ available }}
26 | 27 | 29 | {% trans "Optimize workload for those missions" %} 30 | 31 | {% else %} 32 | {% trans "No staffing" %} 33 | {% endif %} --------------------------------------------------------------------------------