Commit 70f6b7d9 by Damien Moulard

merge conflicts

parents 20f4c1e3 b3184aff
......@@ -47,6 +47,11 @@ PAYZEN_DEBUG=false
### TAV ###
TAV_ENV=0
PRESTA_SELF_INIT_AND_EVAL=0
AUTOMATISATION_RECONVERSION=0
PRESTA_EXTRA_DATA=0
HOUSEHOLD_BASED_ALLOWANCE=0
SSA_FRIENDLY_FLUX_TYPE_NAMES=0
FIX_BALANCE_ADHERENT_PASSWORD=ChangeMe
EMAIL_ERROR=technique@kohinos.net
EMAIL_USER_FROM=noreply@kohinos.fr
......
......@@ -66,3 +66,7 @@ templates/themes/custom/
#INTELLIJ
.idea/
#dir containg exported reconversions xml
reconversions/*
!reconversions/.versionme
......@@ -15,6 +15,7 @@ Extensions PHP :
iconv
mariadb (ou mysql > 8.0)
gd
bcmath (en environnement tav/ssa)
Installer composer si besoin
......@@ -54,6 +55,10 @@ Copier le fichier .env.dist en .env et configurer :
- la variable APP_SECRET (variable secrète que vous pouvez générer à partir de cette url : http://nux.net/secret
- s'il s'agit d'une instance TAV, mettre la variable TAV_ENV à 1 (sinon la laisser à zéro)
- en environnement TAV, pour activer le parcours d'inscription autonomisé qui intègre la réponse à une questionnaire d'auto-évaluation pour les points de vente, passer PRESTA_SELF_INIT_AND_EVAL à 1
- en environnement TAV, la variable AUTOMATISATION_RECONVERSION permet d'activer l'automatisation des reconversions
- en environnement TAV, la variable PRESTA_EXTRA_DATA permet d'indiquer puis d'afficher publiquement plus de données concernant les prestataires (e.g. familles de produits)
- en environnement TAV, la variable HOUSEHOLD_BASED_ALLOWANCE permet d'activer un mode de cotisations libres et d'allocations basées sur la composition du foyer
- en environnement TAV, la variable SSA_FRIENDLY_FLUX_TYPE_NAMES permet d'adapter le nommage des types de flux à un projet de type ssa
Si vous utilisez Payzen comme moyen de paiement par CB :
......@@ -82,13 +87,12 @@ Création des tables et des contraintes :
Charger les fixtures standards :
Pour une instance non TAV :
Pour une instance non SSA :
**$ php bin/console hautelook:fixtures:load --purge-with-truncate --env=pro**
Pour une instance TAV :
Pour une instance TAV, ou bien :
**$ php bin/console hautelook:fixtures:load --purge-with-truncate --env=tavpro**
ou bien :
**$ php bin/console hautelook:fixtures:load --purge-with-truncate --env=ssagirondepro**
Vous obtiendrez cette erreur ci dessous, c'est normal !
......@@ -97,13 +101,13 @@ There is no main category related to context: rubrique
**$ php bin/console sonata:media:fix-media-context**
Pour une instance non TAV :
Pour une instance non SSA :
**$ php bin/console hautelook:fixtures:load --append --env=pro**
Pour une instance TAV :
Pour une instance TAV, ou bien :
**$ php bin/console hautelook:fixtures:load --append --env=tavpro**
ou bien :
**$ php bin/console hautelook:fixtures:load --append --env=ssagirondepro**
Supprimer le cache (si besoin)
......@@ -145,7 +149,9 @@ Une fois tout installé, éventuellement changer le logo et favicon situés dans
Une fois Payzen configuré sur le Kohinos, il faut le configurer sur le Back Office de Payzen :
Paramétrage -> Boutique : configurer les 2 "URL de retour de la boutique" avec la page d'accueil du site.
Paramétrage -> Règles de notification -> URL de notification à la fin du paiement : dans le 2ème encadré, renseigner les 2 champs avec [url_du_site]/payment/done
Paramétrage -> Règles de notification -> URL de notification à la fin du paiement : dans le 2ème encadré, renseigner les 2 champs avec [url_du_site]/payment/notify
Paramétrage -> Règles de notification -> URL de notification à la création d'un abonnement (mal nommé à mon avis car utilisé à chaque échéance) : dans le 2ème encadré, renseigner les 2 champs avec [url_du_site]/payment/notify
Remarque : les URLS de notification sont réécrites par Payum à la même valeur dans le cadre de sa configuration par défaut
Une fois l'installation et la configuration du Kohinos terminées, il est conseillé de réaliser un premier paiement afin de tester le bon fonctionnement de la communication avec Payzen en environnement de production.
......
......@@ -2155,3 +2155,13 @@ https://github.com/sonata-project/SonataAdminBundle/issues/4022
height: 0.1px !important;
opacity: 0.01 !important;
}
.prestataire-products-families-row {
display: flex;
gap: 10px;
}
.prestataire-products-families-row .form-group {
flex-basis: 50%;
margin-bottom: 5px;
}
\ No newline at end of file
......@@ -111,7 +111,7 @@ legend.required:after {
margin-bottom: 20px;
}
.formEncaisserCotisationAdherent-no-profile {
.formEncaisserCotisationAdherent-no-cotisation-amount {
font-style: italic;
color: #ff4136;
}
......@@ -291,6 +291,17 @@ form[name="formEncaissement"] label {
}
}
/**
* END Success check animation.
*/
#display-balance-text-container {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
}
#login-password-container {
position: relative;
}
......@@ -330,6 +341,96 @@ form[name="formEncaissement"] label {
border-color: transparent transparent black transparent;
}
.global-evaluation-container {
background-color:powderblue;
border-radius:10px;
border: 5px solid powderblue;
}
.review-container {
background-color:#CBC3E3;
border-radius:10px;
border: 5px solid #CBC3E3;
}
/**
* END Success check animation.
*/
.prestaquizz_temporary_save_container {
position: fixed;
bottom: 0;
right: 1rem;
z-index: 2;
}
.presta-products-family {
display: flex;
margin-bottom: 1rem;
}
.presta-products-family-fields {
width: 95%;
}
.presta-products-family-delete {
width: 5%;
text-align: end;
}
.presta-products-family-form-group {
display: flex;
gap: 5px;
align-items: center;
margin-bottom: 0.25rem !important;
}
.presta-products-family-form-group label {
width: 40%;
margin-bottom: 0;
}
.delete-icon {
color: #ff4136;
}
.delete-icon:hover {
cursor: pointer;
color: #a02923;
}
.presta-products-family-duplicate-warning {
display: none;
color: #ff4136;
margin-top: -0.25rem;
text-align: right;
font-style: italic;
}
.presta-conventionnement-applied-text {
margin-top: -0.75rem !important;
margin-bottom: 1rem;
font-size: 100%;
}
.montant-panier-error {
margin-top: -0.75rem !important;
margin-bottom: 1rem;
font-size: 100%;
color: #ff4136;
}
.input-error {
border-color: #ff4136 !important;
}
.input-error:focus {
box-shadow: 0 0 0 0.2rem #ff403667 !important;
}
.text-error {
color: #ff4136;
}
.tav-cotisation-payment-form {
display: none;
margin-top: 1rem;
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ require('../css/admin.css');
require('bootstrap');
require('../css/common.css');
require('../js/geoloc.js');
const $ = require("jquery");
$('#flash-messages').flashNotification('init');
......@@ -70,7 +71,7 @@ $(document).ready(function() {
});
});
// Hide or display Cotisation or Profils de Cotisation submenu depending on TAV env
// Hide or display Cotisation, Profils de Cotisation and Products Families submenu depending on TAV env
const tav_env = document.getElementsByName('is-tav-env')[0].getAttribute('content');
let linksToCotisationAdherent = $('a[href="/admin/cotisation_adherent/list"]');
......@@ -91,6 +92,12 @@ $(document).ready(function() {
linksToProfilDeCotisation[i].parentNode.style.display = (tav_env === "1") ? "" : "none";
}
}
let linksToProductsFamily = $('a[href="/admin/app/productfamily/list"]');
for(let i = 0 ; i < linksToProductsFamily.length ; i++) {
if(linksToProductsFamily[i].innerText === "Familles de produits") {
linksToProductsFamily[i].parentNode.style.display = (tav_env === "1") ? "" : "none";
}
}
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
......@@ -124,4 +131,11 @@ $(document).ready(function() {
}
actionOnRecevoirUnRappelChange();
}
if($(".fixBalanceAdherentFormPart").length > 0) {
$(".fixBalanceAdherentFormPart").parent().parent().hide();
$("div[id$='_idmlc']").append(
"<br/><input type='button' id='showFieldsToFixBalance' onclick='$(\".fixBalanceAdherentFormPart\").parent().parent().show();' value='Afficher les champs pour corriger le solde'> </input>"
);
}
});
......@@ -492,7 +492,90 @@ $(function() {
$("#formEncaissement_payment_code").val('');
// Set payment summary on validation page
$(".payment-recap").text(`${$("#formEncaissement_adherent option:selected").text()}, montant : ${$("#formEncaissement_montant").val()}`);
let recap = `${$("#formEncaissement_adherent option:selected").text()}, montant : ${$("#formEncaissement_montant").val()} ${KOH_MLC_NAME_SMALL}`;
$(".payment-recap").text(recap);
// If conventionnement mode is active
if ($(this).find("#formEncaissement_montantPanier").length > 0) {
$("#formEncaissement_montant").prop("disabled",true);
let maxAmount = 0;
function disableMontant() {
$("#formEncaissement_montant").val("");
$("#formEncaissement_montant").prop("disabled", true);
$(".presta-conventionnement-applied-text").hide();
}
/**
* If basket amount is set and valid, enable emlc amount field & hide errors if needed.
* Else display errors.
*/
function checkMontantPanier() {
let input = $("#formEncaissement_montantPanier");
let val = input.val();
if (val === '') {
// reset
$(".montant-panier-error").hide();
input.removeClass('input-error');
disableMontant();
} else {
let basketAmount = parseFloat(val);
if (isNaN(basketAmount)) {
// display error and reset the rest
$(".montant-panier-error").show();
input.addClass('input-error');
disableMontant();
return;
}
$(".montant-panier-error").hide();
input.removeClass('input-error');
// no error: enable amount field, set max emlc amount and display helper text
$("#formEncaissement_montant").prop("disabled", false);
$(".presta-conventionnement-applied-text").show();
maxAmount = parseFloat(formEncaissementConventionnement) * basketAmount;
$(".encaissment-emlc-max-amount").text(maxAmount);
}
checkMaxAmount();
}
checkMontantPanier();
$("#formEncaissement_montantPanier").on("input", checkMontantPanier);
/**
* Check that emlc amount is valid depending on basket amount
*/
function checkMaxAmount() {
function reset() {
$("#formEncaissement_montant").removeClass('input-error');
$(".encaissment-emlc-max-amount-container").removeClass("text-error");
}
let val = $("#formEncaissement_montant").val();
if (val === '') {
reset();
} else {
let amount = parseFloat(val);
if (!isNaN(amount) && amount > maxAmount) {
$(this).addClass('input-error');
$(".encaissment-emlc-max-amount-container").addClass("text-error");
} else if (!isNaN(amount) && amount <= maxAmount) {
reset();
}
}
}
checkMaxAmount();
$("#formEncaissement_montant").on("input", checkMaxAmount);
}
}
/**
......@@ -541,20 +624,20 @@ $(function() {
if (cotisationmontant === undefined) {
$("#formEncaisserCotisationAdherent-montant-container").hide();
$("#formEncaisserCotisationAdherent-no-profile").hide();
$("#formEncaisserCotisationAdherent-no-cotisation-amount").hide();
return;
}
if (cotisationmontant !== null) {
$("#formEncaisserCotisationAdherent-montant-display").text(`${cotisationmontant} €`);
$("#formEncaisserCotisationAdherent-montant-container").show();
$("#formEncaisserCotisationAdherent-no-profile").hide();
$("#formEncaisserCotisationAdherent-no-cotisation-amount").hide();
$("#formEncaisserCotisationAdherent_save").prop("disabled",false);
} else {
// no cotisation profile
$("#formEncaisserCotisationAdherent-montant-container").hide();
$("#formEncaisserCotisationAdherent-no-profile").show();
$("#formEncaisserCotisationAdherent-no-cotisation-amount").show();
$("#formEncaisserCotisationAdherent_save").prop("disabled",true);
}
......@@ -577,7 +660,7 @@ $(function() {
/**
* Display loader after validating Encaissement form
*/
$("form[name='formEncaissement']").on("submit", (event) => {
$("form[name='formEncaissement']").on("submit", () => {
// non strict equality check
if (formEncaissementValidation == true) {
$("#formEncaissement_save").parent().append('<span class="spinner-border" role="status" aria-hidden="true"></span>');
......@@ -585,6 +668,10 @@ $(function() {
}
})
$("#display-balance-button-no").on("click", () => {
$("#display-balance-text-container").hide();
})
// $('.js-datepicker').datepicker({
// closeText: 'Fermer',
// prevText: '&#x3c;Préc',
......@@ -610,4 +697,135 @@ $(function() {
// showButtonPanel: true
// });
/*
* Improve lisibility when reviewing prestataire questionnaire.
*/
// Get all radio inputs of prestataire questionnaire forms when on review mode (disabled)
var radioInputs = document.querySelectorAll(
'form[name$="SelfEvalPrestaQuiz"] input[type="radio"]:disabled'
);
radioInputs.forEach(function(radioInput) {
if (!radioInput.checked) {
//get the colored icon
icon = radioInput.labels[0].querySelector('i');
var classList = Array.from(icon.classList);
// Filter out classes starting with "text-"
var textClasses = classList.filter(function(className) {
return className.startsWith('text-');
});
// Remove all classes starting with "text-"
textClasses.forEach(function(textClass) {
icon.classList.remove(textClass);
});
}
});
// Reset hidden data on prestataire self evaluation quizz, for definitive form submition
$('#formDistributorSelfEvalPrestaQuiz_submit_presta_quizz').val('1');
/**
* On prestataire self evaluation quizz, remove all required attributes in form on click on the temporary save button.
* Set form hidden data to not definitively submit the form.
*/
$("#formDistributorSelfEvalPrestaQuiz_temporary_save").on("click", (e) => {
e.preventDefault();
$('form[name="formDistributorSelfEvalPrestaQuiz"] input').removeAttr('required');
$('#formDistributorSelfEvalPrestaQuiz_submit_presta_quizz').val('0');
$('form[name="formDistributorSelfEvalPrestaQuiz"]').trigger("submit");
});
/**
* Delete a PrestataireProductFamily node on icon click
* @param {Event} e
*/
function deletePrestataireProductFamily(e) {
e.preventDefault();
$(this).closest('.presta-products-family')
.fadeOut()
.remove();
}
$(".presta-products-family-delete").on("click", deletePrestataireProductFamily);
/**
* Check if a product family is already selected on select change.
*/
function onPrestataireProductFamilyChange() {
let current_select_id = $(this).attr('id');
let current_select_selected_option = $( `#${current_select_id} option:selected` ).text();
let same_selected_option = false;
$('.prestataire-product-families-select').each(function() {
let select_id = $(this).attr('id');
let select_selected_option = $( `#${select_id} option:selected` ).text();
if (current_select_id !== select_id && current_select_selected_option === select_selected_option) {
same_selected_option = true;
return false;
}
});
$('.presta-products-family-fields .presta-products-family-duplicate-warning').hide();
$('.prestataire-product-families-products').attr('disabled', false);
if (same_selected_option) {
$(this).closest('.presta-products-family-fields').find('.presta-products-family-duplicate-warning').show();
$(this).closest('.presta-products-family-fields').find('.prestataire-product-families-products').attr('disabled', true);
}
}
$('.prestataire-product-families-select').on('change', onPrestataireProductFamilyChange);
/**
* Twig helper, add element to Collection in template based on collection prototype
* @param {*} e
*/
function addFormToCollection(e) {
const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);
const item = document.createElement('div');
item.innerHTML = collectionHolder
.dataset
.prototype
.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index++;
$(".presta-products-family-delete").off("click", deletePrestataireProductFamily);
$(".presta-products-family-delete").on("click", deletePrestataireProductFamily);
$('.prestataire-product-families-select').off('change', onPrestataireProductFamilyChange);
$('.prestataire-product-families-select').on('change', onPrestataireProductFamilyChange);
};
$("#add-prestataire-products-family").on("click", addFormToCollection);
$("#tav-cotisation-payment-type").on("change", function() {
$(".tav-cotisation-payment-form").hide();
if (this.value === "instant-payment") {
$("#tav-cotisation-instant-payment-container").show();
} else if (this.value === "recurrent-payment") {
$("#tav-cotisation-recurrent-payment-container").show();
}
});
$("input:text[name='formAchatMonnaieAdherentRecurrent[don][montant]']").on('input', function() {
let montant = 0;
montant = parseFloat($('input#formAchatMonnaieAdherentRecurrent_montant').val());
let montantDon = parseFloat($("input:text[name='formAchatMonnaieAdherentRecurrent[don][montant]']").val().replace(",", "."));
if (isNaN(montantDon)) {
montantDon = 0;
}
var valuetotal = montant + montantDon;
$("span.achat_monnaie_premier_montant_total").text(valuetotal + ' €')
});
});
......@@ -19,6 +19,7 @@
"ext-intl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"andrew-svirin/ebics-client-php": "^2.1",
"api-platform/core": "^2.6",
"beberlei/doctrineextensions": "^1.3",
"composer/package-versions-deprecated": "1.*",
......@@ -30,6 +31,7 @@
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/migrations": "^3.1",
"doctrine/orm": "^2.7",
"ekyna/payum-payzen": "@dev",
"friendsofsymfony/user-bundle": "^2.1",
"gamez/ramsey-uuid-normalizer": "^2.1",
"geocoder-php/cache-provider": "^4.3",
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a59b75dfa4cd00a3ccefd4cda875dd52",
"content-hash": "9f1667eb8d3defce6dc7895876f07cd5",
"packages": [
{
"name": "alcohol/iso4217",
......@@ -60,6 +60,82 @@
"time": "2019-10-30T10:03:42+00:00"
},
{
"name": "andrew-svirin/ebics-client-php",
"version": "v2.1.2",
"source": {
"type": "git",
"url": "https://github.com/andrew-svirin/ebics-client-php.git",
"reference": "1692d4d5f2daab29c62bef6c54a0258d6fc927d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/andrew-svirin/ebics-client-php/zipball/1692d4d5f2daab29c62bef6c54a0258d6fc927d6",
"reference": "1692d4d5f2daab29c62bef6c54a0258d6fc927d6",
"shasum": ""
},
"require": {
"ext-bcmath": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-json": "*",
"ext-openssl": "*",
"ext-zip": "*",
"ext-zlib": "*",
"php": "^7.4 || ^8"
},
"require-dev": {
"andrew-svirin/cfonb-php": "dev-master",
"andrew-svirin/mt942-php": "dev-master",
"mpdf/mpdf": "~8.0.17",
"phpseclib/phpseclib": "~2.0.35",
"phpstan/phpstan": "~1.9.17",
"phpunit/phpunit": "~9.6.3",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"squizlabs/php_codesniffer": "~3.7.1"
},
"suggest": {
"andrew-svirin/cfonb-php": "If you need to parse format CFONB from FDL requests.",
"andrew-svirin/mt942-php": "If you need to parse format MT942 from VMK, STA requests.",
"mpdf/mpdf": "If you need to generate PDF file letter for Bank.",
"psr/http-client": "If you want use the PsrHttpClient",
"psr/http-factory": "If you want use the PsrHttpClient"
},
"type": "library",
"autoload": {
"psr-4": {
"AndrewSvirin\\Ebics\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andrew Svirin"
}
],
"description": "PHP library to communicate with bank through EBICS protocol.",
"keywords": [
"cfonb.120",
"cfonb.240",
"client",
"ebics",
"mt940",
"mt942",
"openssl",
"php",
"rsa",
"x509"
],
"support": {
"issues": "https://github.com/andrew-svirin/ebics-client-php/issues",
"source": "https://github.com/andrew-svirin/ebics-client-php/tree/v2.1.2"
},
"time": "2023-08-16T18:19:58+00:00"
},
{
"name": "api-platform/core",
"version": "v2.6.8",
"source": {
......@@ -2450,7 +2526,7 @@
},
{
"name": "ekyna/payum-payzen",
"version": "dev-master",
"version": "dev-5691-paiement-reccurent-payzen",
"dist": {
"type": "path",
"url": "./lib/ekyna/payum-payzen",
......
......@@ -19,6 +19,7 @@ framework:
cookie_secure: 'auto'
cookie_samesite: 'lax'
# name: Kohinossessid
gc_probability: null
#esi: true
#fragments: true
......
......@@ -62,7 +62,7 @@ security:
entry_point: App\Security\LoginAuthenticator
remember_me:
secret: "%kernel.secret%"
lifetime: 1800
lifetime: 604800
path: /
domain: ~
user_provider: fos_userbundle
......
......@@ -331,6 +331,14 @@ sonata_admin:
icon: '<i class="fa fa-upload"></i>'
items:
- admin.import
sonata.admin.productsFamily:
keep_open: false
on_top: true
label: "Familles de produits"
label_catalogue: SonataAdminBundle
icon: '<i class="fa fa-shopping-basket"></i>'
items:
- admin.productsFamily
sonata.admin.group.globalparameter:
keep_open: false
on_top: true
......
......@@ -13,3 +13,7 @@ twig:
globals:
tav_env: '%env(TAV_ENV)%'
presta_self_init_and_eval: '%env(PRESTA_SELF_INIT_AND_EVAL)%'
presta_extra_data: '%env(PRESTA_EXTRA_DATA)%'
household_based_allowance: '%env(HOUSEHOLD_BASED_ALLOWANCE)%'
automatisation_reconversion: '%env(AUTOMATISATION_RECONVERSION)%'
ssa_friendly_flux_type_names: '%env(SSA_FRIENDLY_FLUX_TYPE_NAMES)%'
\ No newline at end of file
......@@ -14,6 +14,12 @@ parameters:
sonata.media.admin.gallery.class: 'App\Admin\GalleryAdmin'
tav_env: '%env(TAV_ENV)%'
presta_self_init_and_eval: '%env(PRESTA_SELF_INIT_AND_EVAL)%'
automatisation_reconversion: '%env(AUTOMATISATION_RECONVERSION)%'
presta_extra_data: '%env(PRESTA_EXTRA_DATA)%'
household_based_allowance: '%env(HOUSEHOLD_BASED_ALLOWANCE)%'
ssa_friendly_flux_type_names: '%env(SSA_FRIENDLY_FLUX_TYPE_NAMES)%'
fix_balance_adherent_password: '%env(FIX_BALANCE_ADHERENT_PASSWORD)%'
# PARAMETRES DES IMPORTS POSSIBLE POUR L'APPLICATION DE GESTION DE MONNAIE LOCALE COMPLEMENTAIRE
app.import.separator: ';'
......@@ -125,6 +131,10 @@ services:
public: true
arguments: ['@app.utils.custom_entity_manager', '@security.helper', '@app.utils.operations']
app.utils.payment:
class: App\Utils\PaymentUtils
autowire: true
app.twig.main.extension:
class: App\Twig\AppExtension
autowire: false
......@@ -249,7 +259,7 @@ services:
admin.adherent.gerer:
class: App\Admin\AdherentAdmin
# arguments: [~, App\Entity\OBJECT, 'PixSortableBehaviorBundle:SortableAdmin']
arguments: [~, App\Entity\Adherent, 'App\Controller\CRUD\CRUDController']
arguments: [~, App\Entity\Adherent, 'App\Controller\AdherentAdminController']
tags:
- name: sonata.admin
manager_type: orm
......@@ -263,6 +273,7 @@ services:
- [ setUserManager, ['@fos_user.user_manager']]
- [ setSecurity, ['@security.helper']]
- [ setEventDispatcher, ['@event_dispatcher']]
- [ setTavCotisationUtils, ['@app.utils.tav_cotisations']]
admin.all.cotisations:
class: App\Admin\CotisationAdmin
......@@ -658,6 +669,16 @@ services:
show_mosaic_button: false
public: true
admin.productsFamily:
class: App\Admin\ProductFamilyAdmin
arguments: [~, App\Entity\ProductFamily, ~]
tags:
- name: sonata.admin
manager_type: orm
group: "Familles de Produits"
label: "Familles de Produits"
public: true
sonata.media.provider.csv:
class: App\Admin\ImportProvider
tags:
......
# Configuration des données initiales
App\Entity\EtatPrestataire:
etatSuspendu:
enabled: true
name: 'Suspendu'
content: 'Prestataires suspendus pour diverses raisons'
etatRappel:
enabled: true
name: 'Rappel'
content: 'Prestataires à rappeler'
etatAnnule:
enabled: true
name: 'Annulé'
content: 'Prestataires annulé'
etatProspect:
enabled: true
name: 'Prospects'
content: 'Prospects'
App\Entity\TypePrestataire:
typepresta1:
name: 'Prestataire'
typepresta2:
name: 'Partenaire'
App\Entity\Usergroup:
usergroup_adherent:
__construct: ['Adherent', ['ROLE_ADHERENT']]
usergroup_prestataire:
__construct: ['Prestataire', ['ROLE_PRESTATAIRE']]
usergroup_caissier:
__construct: ['Caissier', ['ROLE_CAISSIER']]
usergroup_adminsiege:
__construct: ['Administrateur du Siege', [
'ROLE_ADMIN_SIEGE',
'ROLE_SONATA_USER_ADMIN_USER_ALL',
'ROLE_SONATA_USER_ADMIN_GROUP_ALL',
'ROLE_ADMIN_COMPTOIR_GERER_LIST',
'ROLE_ADMIN_COMPTOIR_GERER_VIEW',
'ROLE_ADMIN_ADHERENT_GERER_ALL',
'ROLE_ADMIN_ALL_COTISATIONS_ALL',
'ROLE_ADMIN_ALL_ACHATSMONNAIE_ALL',
'ROLE_ADMIN_GROUPE_GERER_ALL',
'ROLE_ADMIN_GLOBALPARAMETER_GERER_ALL',
'ROLE_ADMIN_TRANSFERT_GERER_ALL',
'ROLE_ADMIN_ALL_DEMANDE_ACHATSMONNAIE_ALL',
'ROLE_ADMIN_OPERATION_PRESTATAIRE_GERER_LIST',
'ROLE_ADMIN_OPERATION_ADHERENT_GERER_LIST',
'ROLE_ADMIN_OPERATION_COMPTOIR_GERER_LIST',
'ROLE_ADMIN_OPERATION_GROUPE_GERER_LIST',
'ROLE_ADMIN_OPERATION_SIEGE_GERER_LIST',
'ROLE_ADMIN_ADHERENT_COTISATIONS_ALL',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_ALL',
'ROLE_ADMIN_ALL_COTISATIONS_ALL',
'ROLE_ADMIN_HELLOASSO_ALL',
'ROLE_ADMIN_DONS_ALL']]
usergroup_redacteur:
__construct: ['Rédacteur', [
'ROLE_REDACTEUR',
'ROLE_SONATA_MEDIA_ADMIN_MEDIA_ALL',
'ROLE_SONATA_MEDIA_ADMIN_GALLERY_ALL',
'ROLE_SONATA_MEDIA_ADMIN_GALLERY_HAS_MEDIA_ALL',
'ROLE_PRODIGIOUS_SONATA_MENU_ADMIN_MENU_ALL',
'ROLE_PRODIGIOUS_SONATA_MENU_ADMIN_MENU_ITEM_ALL',
'ROLE_ADMIN_NEWS_GERER_ALL',
'ROLE_ADMIN_DOCUMENT_GERER_ALL',
'ROLE_ADMIN_RUBRIQUE_GERER_ALL',
'ROLE_ADMIN_FAQ_GERER_ALL',
'ROLE_ADMIN_PAGE_GERER_ALL',
'ROLE_ADMIN_TRADUCTION_GERER_ALL']]
usergroup_controleur:
__construct: ['Contrôleur', [
'ROLE_CONTROLEUR',
'ROLE_ADMIN_TRANSACTION_GERER_LIST',
'ROLE_ADMIN_TRANSACTION_GERER_VIEW',
'ROLE_ADMIN_OPERATION_PRESTATAIRE_GERER_LIST',
'ROLE_ADMIN_OPERATION_ADHERENT_GERER_LIST']]
usergroup_tresorier:
__construct: ['Trésorier', [
'ROLE_TRESORIER',
'ROLE_ADMIN_COMPTOIR_GERER_LIST',
'ROLE_ADMIN_COMPTOIR_GERER_VIEW',
'ROLE_ADMIN_ALL_COTISATIONS_ALL',
'ROLE_ADMIN_ALL_ACHATSMONNAIE_ALL',
'ROLE_ADMIN_RECONVERSION_GERER_ALL',
'ROLE_ADMIN_TRANSFERT_GERER_ALL',
'ROLE_ADMIN_ALL_DEMANDE_ACHATSMONNAIE_ALL',
'ROLE_ADMIN_OPERATION_PRESTATAIRE_GERER_ALL',
'ROLE_ADMIN_OPERATION_ADHERENT_GERER_ALL',
'ROLE_ADMIN_OPERATION_COMPTOIR_GERER_ALL',
'ROLE_ADMIN_OPERATION_GROUPE_GERER_ALL',
'ROLE_ADMIN_OPERATION_SIEGE_GERER_ALL',
'ROLE_ADMIN_HELLOASSO_ALL',
'ROLE_ADMIN_DONS_ALL',
'ROLE_ADMIN_ADHERENT_GERER_LIST']]
usergroup_gestiongroupe:
__construct: ['Gestionnaire de Groupe', [
'ROLE_GESTION_GROUPE',
'ROLE_SONATA_USER_ADMIN_USER_ALL',
'ROLE_ADMIN_ADHERENT_GERER_ALL',
'ROLE_ADMIN_ADHERENT_COTISATIONS_ALL',
'ROLE_ADMIN_PRESTATAIRE_GERER_ALL',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_ALL',
'ROLE_ADMIN_GROUPE_GERER_EDIT',
'ROLE_ADMIN_GROUPE_GERER_VIEW',
'ROLE_ADMIN_COMPTOIR_GERER_ALL',
'ROLE_ADMIN_GROUPEPRESTA_GERER_ALL',
'ROLE_ADMIN_DOCUMENT_GERER_ALL',
'ROLE_ADMIN_TRANSFERT_GERER_EDIT',
'ROLE_ADMIN_TRANSFERT_GERER_LIST',
'ROLE_ADMIN_TRANSFERT_GERER_CREATE',
'ROLE_ADMIN_TRANSFERT_GERER_EXPORT',
'ROLE_ADMIN_OPERATION_PRESTATAIRE_GERER_LIST',
'ROLE_ADMIN_OPERATION_ADHERENT_GERER_LIST',
'ROLE_ADMIN_OPERATION_COMPTOIR_GERER_LIST']]
usergroup_comptoir:
__construct: ['Comptoir', [
'ROLE_COMPTOIR',
'ROLE_ADMIN_ADHERENT_GERER_EDIT',
'ROLE_ADMIN_ADHERENT_GERER_LIST',
'ROLE_ADMIN_ADHERENT_GERER_CREATE',
'ROLE_ADMIN_ADHERENT_GERER_VIEW',
'ROLE_ADMIN_ADHERENT_COTISATIONS_ALL',
'ROLE_ADMIN_COMPTOIR_GERER_EDIT',
'ROLE_ADMIN_COMPTOIR_GERER_VIEW',
'ROLE_ADMIN_TRANSFERT_GERER_LIST',
'ROLE_ADMIN_TRANSFERT_GERER_CREATE',
'ROLE_ADMIN_TRANSFERT_GERER_VIEW',
'ROLE_ADMIN_PRESTATAIRE_GERER_VIEW',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_VIEW',
'ROLE_ADMIN_PRESTATAIRE_GERER_LIST',
'ROLE_ADMIN_PRESTATAIRE_GERER_EDIT',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_EDIT',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_LIST',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_CREATE']]
usergroup_contact:
__construct: ['Contact', [
'ROLE_CONTACT',
'ROLE_ADMIN_ADHERENT_GERER_ALL',
'ROLE_ADMIN_ADHERENT_COTISATIONS_ALL',
'ROLE_ADMIN_PRESTATAIRE_GERER_ALL',
'ROLE_ADMIN_PRESTATAIRE_COTISATIONS_ALL',
'ROLE_ADMIN_COMPTOIR_GERER_ALL',
'ROLE_ADMIN_GROUPEPRESTA_GERER_ALL',
'ROLE_ADMIN_NEWS_GERER_ALL',
'ROLE_ADMIN_DOCUMENT_GERER_ALL',
'ROLE_ADMIN_RUBRIQUE_GERER_ALL']]
usergroup_superadmin:
__construct: ['Super Admin', ['ROLE_SUPER_ADMIN']]
Prodigious\Sonata\MenuBundle\Entity\Menu:
menu_main:
name: 'Main menu'
alias: 'main'
Prodigious\Sonata\MenuBundle\Entity\MenuItem:
menuitem4:
menu: '@menu_main'
name: 'Points de vente'
url: '#'
position: 1
target: 0
enabled: 1
menuitem5:
menu: '@menu_main'
name: 'Carte des prestataires'
parent: '@menuitem4'
url: '/prestataires/carte'
position: 0
target: 0
enabled: 1
menuitem6:
menu: '@menu_main'
name: 'Liste des prestataires'
parent: '@menuitem4'
url: '/prestataires/liste'
position: 1
target: 0
enabled: 1
menuitem7:
menu: '@menu_main'
name: 'Liste des partenaires'
parent: '@menuitem4'
url: '/partenaires/liste'
position: 2
target: 0
enabled: 1
menuitem8:
menu: '@menu_main'
name: 'Rubriques'
parent: '@menuitem4'
url: '/prestataires/rubriques'
position: 3
target: 0
enabled: 1
menuitem9:
menu: '@menu_main'
name: 'Comptoirs des habitants'
url: '#'
position: 2
target: 0
enabled: 1
menuitem10:
menu: '@menu_main'
name: 'Carte des comptoirs'
parent: '@menuitem9'
url: '/comptoirs/carte'
position: 0
target: 0
enabled: 1
menuitem11:
menu: '@menu_main'
name: 'Liste des comptoirs'
parent: '@menuitem9'
url: '/comptoirs/liste'
position: 1
target: 0
enabled: 1
menuitem12:
menu: '@menu_main'
name: 'Carte des amaps'
parent: '@menuitem9'
url: '/groupe/prestataires/amap/carte'
position: 2
target: 0
enabled: 1
menuitem13:
menu: '@menu_main'
name: 'Carte des marchés'
parent: '@menuitem9'
url: '/groupe/prestataires/marche/carte'
position: 3
target: 0
enabled: 1
menuitem14:
menu: '@menu_main'
name: 'Aide'
url: '#'
position: 3
target: 0
enabled: 1
menuitem15:
menu: '@menu_main'
name: 'Foire aux questions'
parent: '@menuitem14'
url: '/faq'
position: 0
target: 0
enabled: 1
menuitem16:
menu: '@menu_main'
name: 'Nous contacter'
parent: '@menuitem14'
url: '/contact'
position: 1
target: 0
enabled: 1
menuitem17:
menu: '@menu_main'
name: 'Actualités'
url: '/news'
position: 4
target: 0
enabled: 1
App\Entity\Subterritory:
subterritory1:
name: "Bordeaux Nord / Bordeaux La Benauge"
subterritory2:
name: "Pays foyen"
subterritory3:
name: "Bègles"
subterritory4:
name: "Sud Gironde / Bazadais"
App\Application\Sonata\MediaBundle\Entity\Media:
media6:
name: 'wosmpl-marker-icon17-produits-de-lagriculture-et-elevage.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: e5d9701e23fcfa5822d7c93092a522f82cf970ad.png
provider_metadata: {"filename":"wosmpl-marker-icon17-produits-de-lagriculture-et-elevage.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 29446
context: 'rubrique'
media7:
name: 'wosmpl-marker-icon3-metier-darts-creation.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 3b56c41033edbc04176b2bc943716820b8d5859e.png
provider_metadata: {"filename":"wosmpl-marker-icon3-metier-darts-creation.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 38284
context: 'rubrique'
media8:
name: 'wosmpl-marker-icon-commerce-alimentaire.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 3f7b75fef4df63756fd425d23fe90fce04757011.png
provider_metadata: {"filename":"wosmpl-marker-icon-commerce-alimentaire.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 33137
context: 'rubrique'
media9:
name: 'wosmpl-marker-icon2-restaurant-bar-traiteur.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 6eaa4bbdf1225932d30c5ae54fb9996658836d7d.png
provider_metadata: {"filename":"wosmpl-marker-icon2-restaurant-bar-traiteur.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 32622
context: 'rubrique'
media10:
name: 'wosmpl-marker-icon4-habillement-mode-accessoires.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 91f22423eaf61b22e7365250d902efecdce698fc.png
provider_metadata: {"filename":"wosmpl-marker-icon4-habillement-mode-accessoires.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 24911
context: 'rubrique'
media11:
name: 'wosmpl-marker-icon5-higiene-beaute.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 749a4285560be42315ac3c4c888557eedc76e81d.png
provider_metadata: {"filename":"wosmpl-marker-icon5-higiene-beaute.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 25898
context: 'rubrique'
media12:
name: 'wosmpl-marker-icon6-Papeterie-librairie-presse-édition.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: aab9d557c3f77b9682ee6726c059badd89eb9ed8.png
provider_metadata: {"filename":"wosmpl-marker-icon6-Papeterie-librairie-presse-e\u0301dition.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 37730
context: 'rubrique'
media13:
name: 'wosmpl-marker-icon9-sortie-culturelle.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 978c0d269d054f0e4f2e88de31eae8d0f0b44bfa.png
provider_metadata: {"filename":"wosmpl-marker-icon9-sortie-culturelle.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 32317
context: 'rubrique'
media14:
name: 'wosmpl-marker-icon11-santé-bien-etre.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: cf4510e313a050931ffae55a7c72651e765e4145.png
provider_metadata: {"filename":"wosmpl-marker-icon11-sante\u0301-bien-etre.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 36273
context: 'rubrique'
media15:
name: 'wosmpl-marker-icon14-Web-multimédia-communication-imprimerie.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 6286598dea47116e34b4e9635eccf05a3fb9368f.png
provider_metadata: {"filename":"wosmpl-marker-icon14-Web-multime\u0301dia-communication-imprimerie.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 29416
context: 'rubrique'
media16:
name: 'wosmpl-marker-icon15-sport-et-loisirs.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 1ecae95f3729865d8f249b17146d7aaef9d9b5c3.png
provider_metadata: {"filename":"wosmpl-marker-icon15-sport-et-loisirs.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 43002
context: 'rubrique'
media17:
name: 'wosmpl-marker-icon16-formation-education.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 72609fa54f78962259708e4e3ff3da37239b224e.png
provider_metadata: {"filename":"wosmpl-marker-icon16-formation-education.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 31656
context: 'rubrique'
media18:
name: 'wosmpl-marker-icon19-immobillier.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: c1a399604ae2599ffbc5701a1d138830d2ececb7.png
provider_metadata: {"filename":"wosmpl-marker-icon19-immobillier.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 28576
context: 'rubrique'
media19:
name: 'wosmpl-marker-icon21-divers.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 434524af87d1e989f8fd476a838fb6d0243a34f3.png
provider_metadata: {"filename":"wosmpl-marker-icon21-divers.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 25092
context: 'rubrique'
media20:
name: 'wosmpl-marker-icon22-marche-amap.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 348b65dcd5ed271c94bf28eb353b12c6bf4d45ac.png
provider_metadata: {"filename":"wosmpl-marker-icon22-marche-amap.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 34804
context: 'rubrique'
media21:
name: 'wosmpl-marker-icon23-Artisanat-constructions-réparations.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: fc1af9dfa9da71e26464879365203a7d9158bc90.png
provider_metadata: {"filename":"wosmpl-marker-icon23-Artisanat-constructions-re\u0301parations.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 25215
context: 'rubrique'
media22:
name: 'wosmpl-marker-icon20-service-a-la-personne.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: b48e9ed9bf500aabfca8cd70445d2b9274587dfe.png
provider_metadata: {"filename":"wosmpl-marker-icon20-service-a-la-personne.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 34234
context: 'rubrique'
media23:
name: 'wosmpl-marker-icon7-decoration-ameublement-bricolage-jardin.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: e1ea5c5d2a395a0d947ed736a3289fea3fdb821b.png
provider_metadata: {"filename":"wosmpl-marker-icon7-decoration-ameublement-bricolage-jardin.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 39650
context: 'rubrique'
media24:
name: 'wosmpl-marker-icon8-Commerces-divers.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 3433854a2e8d5bf720db32631533afe54600ec87.png
provider_metadata: {"filename":"wosmpl-marker-icon8-Commerces-divers.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 35621
context: 'rubrique'
media25:
name: 'wosmpl-marker-icon10-Informatique-Electronique.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: 7a313aa2622f4db659d526e76b24322f64c4f910.png
provider_metadata: {"filename":"wosmpl-marker-icon10-Informatique-Electronique.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 25964
context: 'rubrique'
media26:
name: 'wosmpl-marker-icon13-transports-livraison.png'
enabled: true
provider_name: 'sonata.media.provider.image'
provider_status: true
provider_reference: ca83dd7189873cf07639668b48e4d6e940745dda.png
provider_metadata: {"filename":"wosmpl-marker-icon13-transports-livraison.png"}
width: 512
height: 512
content_type: 'image/png'
content_size: 31625
context: 'rubrique'
App\Entity\Rubrique:
rubrique1:
name: "Produit de l'agriculture et élevage"
content: ""
enabled: true
media: '@media6'
rubrique2:
name: "Sports et loisirs"
content: ""
enabled: true
media: '@media16'
rubrique3:
name: "Commerce alimentaire"
content: ""
enabled: true
media: '@media8'
rubrique4:
name: "Restaurant, bar, traiteur"
content: ""
enabled: true
media: '@media9'
rubrique5:
name: "Commerces divers"
content: ""
enabled: true
media: '@media24'
rubrique6:
name: "Métiers d'art, créations"
content: ""
enabled: true
media: '@media7'
rubrique7:
name: "Habillement, mode, accessoires"
content: ""
enabled: true
media: '@media10'
rubrique8:
name: "Papeterie, librairie, presse, édition"
content: ""
enabled: true
media: '@media12'
rubrique9:
name: "Marchés, AMAP"
content: ""
enabled: true
media: '@media20'
rubrique10:
name: "Web, multimédia, communication, imprimerie"
content: ""
enabled: true
media: '@media15'
rubrique11:
name: "Sorties culturelles"
content: ""
enabled: true
media: '@media13'
rubrique12:
name: "Informatique, Electronique"
content: ""
enabled: true
media: '@media25'
rubrique13:
name: "Décoration, ameublement, bricolage, jardin"
content: ""
enabled: true
media: '@media23'
rubrique14:
name: "Artisanat, constructions, réparations"
content: ""
enabled: true
media: '@media21'
rubrique15:
name: "Transports, Livraisons"
content: ""
enabled: true
media: '@media26'
rubrique16:
name: "Divers"
content: ""
enabled: true
media: '@media19'
rubrique17:
name: "Hygiène, beauté"
content: ""
enabled: true
media: '@media11'
rubrique18:
name: "Santé, bien-être"
content: ""
enabled: true
media: '@media14'
rubrique19:
name: "Immobilier"
content: ""
enabled: true
media: '@media18'
rubrique20:
name: "Services à la personne"
content: ""
enabled: true
media: '@media22'
rubrique21:
name: "Formation, éducation"
content: ""
enabled: true
media: '@media17'
\ No newline at end of file
......@@ -61,6 +61,20 @@ class CaptureAction implements ActionInterface, GatewayAwareInterface, GenericTo
);
$model['vads_url_check'] = $notifyToken->getTargetUrl();
}
// Set recurrent payment data if needed
$payment = $request->getFirstModel();
if (true == $payment->getIsRecurrent()) {
$model['vads_page_action'] = 'REGISTER_PAY_SUBSCRIBE';
$model['vads_sub_amount'] = strval($payment->getRecurrenceAmount()); // 1000 for 10.00 EUR
$model['vads_sub_currency'] = $model['vads_currency'];
$model['vads_sub_effect_date'] = (new \DateTime('tomorrow', new \DateTimeZone('UTC')))->format('Ymd'); // tomorrow, to avoid duplicate payment this day
//FOR TEST : $model['vads_sub_desc'] = 'RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2';
$count = $payment->getRecurrenceMonthsCount() - 1; //initial payment is not considered by payzen as the first occurence
$monthDay = $payment->getRecurrenceMonthDay();
$model['vads_sub_desc'] = "RRULE:FREQ=MONTHLY;COUNT={$count};BYMONTHDAY={$monthDay}";
}
}
if (false == $model['vads_trans_id']) {
......
......@@ -126,7 +126,6 @@ class Api
$data = $this
->getRequestOptionsResolver()
->resolve(array_replace($data, [
'vads_page_action' => 'PAYMENT',
'vads_version' => 'V2',
]));
......@@ -353,6 +352,10 @@ class Api
'vads_url_return' => null, // Obligatoire si acquisition de la carte par commerçant
'vads_user_info' => null,
'vads_version' => 'V2',
'vads_sub_amount' => null,
'vads_sub_currency' => null,
'vads_sub_effect_date' => null,
'vads_sub_desc' => null,
])
->setRequired([
'vads_amount',
......@@ -365,7 +368,7 @@ class Api
->setAllowedValues('vads_action_mode', ['SILENT', 'INTERACTIVE'])
->setAllowedValues('vads_currency', $this->getCurrencyCodes())
->setAllowedValues('vads_language', $this->getLanguageCodes())
->setAllowedValues('vads_page_action', 'PAYMENT')
->setAllowedValues('vads_page_action', ['PAYMENT', 'REGISTER_PAY_SUBSCRIBE'])
->setAllowedValues('vads_payment_cards', $this->getCardsCodes())
->setAllowedValues('vads_payment_config', function ($value) {
if ($value === 'SINGLE') {
......
......@@ -3359,9 +3359,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001491",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001491.tgz",
"integrity": "sha512-17EYIi4TLnPiTzVKMveIxU5ETlxbSO3B6iPvMbprqnKh4qJsQGk5Nh1Lp4jIMAE0XfrujsJuWZAM3oJdMHaKBA==",
"version": "1.0.30001589",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz",
"integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==",
"dev": true,
"funding": [
{
......@@ -12880,9 +12880,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001491",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001491.tgz",
"integrity": "sha512-17EYIi4TLnPiTzVKMveIxU5ETlxbSO3B6iPvMbprqnKh4qJsQGk5Nh1Lp4jIMAE0XfrujsJuWZAM3oJdMHaKBA==",
"version": "1.0.30001589",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz",
"integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==",
"dev": true
},
"chalk": {
......
......@@ -13,6 +13,7 @@
<server name="SYMFONY_PHPUNIT_REMOVE" value=""/>
<server name="SYMFONY_PHPUNIT_VERSION" value="7.5"/>
<server name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" />
<env name="KERNEL_CLASS" value="App\Kernel"/>
</php>
<testsuites>
<testsuite name="Project Test Suite">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
.traductions .container{max-width:100%;width:100%}.main-header .sidebar-front{background-color:transparent;background-image:none;border-right:1px solid #eee;color:#3c8dbc;float:left;padding:15px}.inputsuccess{border:1px solid #28b62c!important}.inputerror{border:1px solid #ff4136!important}.leaflet-container{z-index:4}.card{-webkit-box-orient:vertical;-webkit-box-direction:normal;word-wrap:break-word;background-clip:border-box;background-color:#fff;border:1px solid rgba(0,0,0,.125);border-radius:.25rem;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative}.card>hr{margin-left:0;margin-right:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125);margin-bottom:0;padding:.75rem 1.25rem}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125);padding:.75rem 1.25rem}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{border-bottom:0;margin-bottom:-.75rem}.card-header-pills,.card-header-tabs{margin-left:-.625rem;margin-right:-.625rem}.card-img-overlay{bottom:0;left:0;padding:1.25rem;position:absolute;right:0;top:0}.card-img{border-radius:calc(.25rem - 1px);width:100%}.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);width:100%}.card-img-bottom{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px);width:100%}.card-deck{-webkit-box-orient:vertical;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-webkit-box-orient:horizontal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-left:-15px;margin-right:-15px}.card-deck,.card-deck .card{-webkit-box-direction:normal}.card-deck .card{-webkit-box-flex:1;-webkit-box-orient:vertical;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-bottom:0;margin-left:15px;margin-right:15px}}.card-group{-webkit-box-orient:vertical;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{border-left:0;margin-left:0}.card-group>.card:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.main-header .sidebar-toggle:before{content:""!important}.flash-messages .alert{background-color:#000;background-color:rgba(30,30,30,.9);background-position:15px 50%;background-repeat:no-repeat;border:0;box-shadow:4px 3px 15px rgba(0,0,0,.9);color:#eee;display:none;padding-left:65px;text-shadow:1px 1px #000;width:200px;z-index:1000}.flash-messages .alert .close{color:#fff;color:hsla(0,0%,100%,.8);opacity:1;text-shadow:0 1px 0 #000}.logo img{max-width:none!important}@media (max-width:767px){.skin-black .main-header>.logo{background-color:#fff;color:#333}.skin-black .main-header>.logo:hover{background-color:#eee}}.skin-black-light .sidebar-menu>li{-webkit-transition:none;-o-transition:unset;transition:none}.sonata-ba-content select[tabindex="-1"]{display:block!important;height:.1px!important;opacity:.01!important;width:.1px!important}.blockcollapse .card-header:hover{background-color:rgba(21,140,186,.2)!important}.blockcollapse .card-header{border-bottom:0;cursor:pointer}.blockcollapse,.blockcollapse .card-header,.blocksolde,.header .navbar,ol.breadcrumb{border-radius:15px}.btn-primary,.btn-secondary{border-radius:10px}.nounderline:hover{text-decoration:none}
\ No newline at end of file
.traductions .container{max-width:100%;width:100%}.main-header .sidebar-front{background-color:transparent;background-image:none;border-right:1px solid #eee;color:#3c8dbc;float:left;padding:15px}.inputsuccess{border:1px solid #28b62c!important}.inputerror{border:1px solid #ff4136!important}.leaflet-container{z-index:4}.card{-webkit-box-orient:vertical;-webkit-box-direction:normal;word-wrap:break-word;background-clip:border-box;background-color:#fff;border:1px solid rgba(0,0,0,.125);border-radius:.25rem;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative}.card>hr{margin-left:0;margin-right:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125);margin-bottom:0;padding:.75rem 1.25rem}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125);padding:.75rem 1.25rem}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{border-bottom:0;margin-bottom:-.75rem}.card-header-pills,.card-header-tabs{margin-left:-.625rem;margin-right:-.625rem}.card-img-overlay{bottom:0;left:0;padding:1.25rem;position:absolute;right:0;top:0}.card-img{border-radius:calc(.25rem - 1px);width:100%}.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);width:100%}.card-img-bottom{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px);width:100%}.card-deck{-webkit-box-orient:vertical;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-webkit-box-orient:horizontal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-left:-15px;margin-right:-15px}.card-deck,.card-deck .card{-webkit-box-direction:normal}.card-deck .card{-webkit-box-flex:1;-webkit-box-orient:vertical;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-bottom:0;margin-left:15px;margin-right:15px}}.card-group{-webkit-box-orient:vertical;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{border-left:0;margin-left:0}.card-group>.card:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.main-header .sidebar-toggle:before{content:""!important}.flash-messages .alert{background-color:#000;background-color:rgba(30,30,30,.9);background-position:15px 50%;background-repeat:no-repeat;border:0;box-shadow:4px 3px 15px rgba(0,0,0,.9);color:#eee;display:none;padding-left:65px;text-shadow:1px 1px #000;width:200px;z-index:1000}.flash-messages .alert .close{color:#fff;color:hsla(0,0%,100%,.8);opacity:1;text-shadow:0 1px 0 #000}.logo img{max-width:none!important}@media (max-width:767px){.skin-black .main-header>.logo{background-color:#fff;color:#333}.skin-black .main-header>.logo:hover{background-color:#eee}}.skin-black-light .sidebar-menu>li{-webkit-transition:none;-o-transition:unset;transition:none}.sonata-ba-content select[tabindex="-1"]{display:block!important;height:.1px!important;opacity:.01!important;width:.1px!important}.prestataire-products-families-row{display:flex;gap:10px}.prestataire-products-families-row .form-group{flex-basis:50%;margin-bottom:5px}.blockcollapse .card-header:hover{background-color:rgba(21,140,186,.2)!important}.blockcollapse .card-header{border-bottom:0;cursor:pointer}.blockcollapse,.blockcollapse .card-header,.blocksolde,.header .navbar,ol.breadcrumb{border-radius:15px}.btn-primary,.btn-secondary{border-radius:10px}.nounderline:hover{text-decoration:none}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -3,19 +3,19 @@
"app": {
"js": [
"/build/runtime.6ad5c9da.js",
"/build/app.a8f5cd0d.js"
"/build/app.3644f7b2.js"
],
"css": [
"/build/app.9c351632.css"
"/build/app.8a3f698b.css"
]
},
"admin": {
"js": [
"/build/runtime.6ad5c9da.js",
"/build/admin.cee4d78d.js"
"/build/admin.3edfb527.js"
],
"css": [
"/build/admin.5dc0eea7.css"
"/build/admin.4de55830.css"
]
}
}
......
{
"build/app.css": "/build/app.9c351632.css",
"build/app.js": "/build/app.a8f5cd0d.js",
"build/admin.css": "/build/admin.5dc0eea7.css",
"build/admin.js": "/build/admin.cee4d78d.js",
"build/app.css": "/build/app.8a3f698b.css",
"build/app.js": "/build/app.3644f7b2.js",
"build/admin.css": "/build/admin.4de55830.css",
"build/admin.js": "/build/admin.3edfb527.js",
"build/runtime.js": "/build/runtime.6ad5c9da.js",
"build/images/fa-solid-900.svg": "/build/images/fa-solid-900.a838c42a.svg",
"build/images/fa-brands-400.svg": "/build/images/fa-brands-400.05d20183.svg",
......
(()=>{"use strict";var e={913:()=>{try{self["workbox:core:6.5.1"]&&_()}catch(e){}},977:()=>{try{self["workbox:precaching:6.5.1"]&&_()}catch(e){}},80:()=>{try{self["workbox:routing:6.5.1"]&&_()}catch(e){}},873:()=>{try{self["workbox:strategies:6.5.1"]&&_()}catch(e){}}},t={};function s(a){var n=t[a];if(void 0!==n)return n.exports;var r=t[a]={exports:{}};return e[a](r,r.exports,s),r.exports}(()=>{s(913);const e=(e,...t)=>{let s=e;return t.length>0&&(s+=` :: ${JSON.stringify(t)}`),s};class t extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}const a={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},n=e=>[a.prefix,e,a.suffix].filter((e=>e&&e.length>0)).join("-"),r=e=>e||n(a.precache),i=e=>e||n(a.runtime);function c(e,t){const s=t();return e.waitUntil(s),s}s(977);function o(e){if(!e)throw new t("add-to-cache-list-unexpected-type",{entry:e});if("string"==typeof e){const t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}const{revision:s,url:a}=e;if(!a)throw new t("add-to-cache-list-unexpected-type",{entry:e});if(!s){const e=new URL(a,location.href);return{cacheKey:e.href,url:e.href}}const n=new URL(a,location.href),r=new URL(a,location.href);return n.searchParams.set("__WB_REVISION__",s),{cacheKey:n.href,url:r.href}}class h{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:s})=>{if("install"===e.type&&t&&t.originalRequest&&t.originalRequest instanceof Request){const e=t.originalRequest.url;s?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return s}}}class l{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{const s=(null==t?void 0:t.cacheKey)||this._precacheController.getCacheKeyForURL(e.url);return s?new Request(s,{headers:e.headers}):e},this._precacheController=e}}let u;async function f(e,s){let a=null;if(e.url){a=new URL(e.url).origin}if(a!==self.location.origin)throw new t("cross-origin-copy-response",{origin:a});const n=e.clone(),r={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},i=s?s(r):r,c=function(){if(void 0===u){const e=new Response("");if("body"in e)try{new Response(e.body),u=!0}catch(e){u=!1}u=!1}return u}()?n.body:await n.blob();return new Response(c,i)}function d(e,t){const s=new URL(e);for(const e of t)s.searchParams.delete(e);return s.href}class p{constructor(){this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t}))}}const g=new Set;s(873);function y(e){return"string"==typeof e?new Request(e):e}class w{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new p,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(const e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){const{event:s}=this;let a=y(e);if("navigate"===a.mode&&s instanceof FetchEvent&&s.preloadResponse){const e=await s.preloadResponse;if(e)return e}const n=this.hasCallback("fetchDidFail")?a.clone():null;try{for(const e of this.iterateCallbacks("requestWillFetch"))a=await e({request:a.clone(),event:s})}catch(e){if(e instanceof Error)throw new t("plugin-error-request-will-fetch",{thrownErrorMessage:e.message})}const r=a.clone();try{let e;e=await fetch(a,"navigate"===a.mode?void 0:this._strategy.fetchOptions);for(const t of this.iterateCallbacks("fetchDidSucceed"))e=await t({event:s,request:r,response:e});return e}catch(e){throw n&&await this.runCallbacks("fetchDidFail",{error:e,event:s,originalRequest:n.clone(),request:r.clone()}),e}}async fetchAndCachePut(e){const t=await this.fetch(e),s=t.clone();return this.waitUntil(this.cachePut(e,s)),t}async cacheMatch(e){const t=y(e);let s;const{cacheName:a,matchOptions:n}=this._strategy,r=await this.getCacheKey(t,"read"),i=Object.assign(Object.assign({},n),{cacheName:a});s=await caches.match(r,i);for(const e of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await e({cacheName:a,matchOptions:n,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(e,s){const a=y(e);var n;await(n=0,new Promise((e=>setTimeout(e,n))));const r=await this.getCacheKey(a,"write");if(!s)throw new t("cache-put-with-no-response",{url:(i=r.url,new URL(String(i),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var i;const c=await this._ensureResponseSafeToCache(s);if(!c)return!1;const{cacheName:o,matchOptions:h}=this._strategy,l=await self.caches.open(o),u=this.hasCallback("cacheDidUpdate"),f=u?await async function(e,t,s,a){const n=d(t.url,s);if(t.url===n)return e.match(t,a);const r=Object.assign(Object.assign({},a),{ignoreSearch:!0}),i=await e.keys(t,r);for(const t of i)if(n===d(t.url,s))return e.match(t,a)}(l,r.clone(),["__WB_REVISION__"],h):null;try{await l.put(r,u?c.clone():c)}catch(e){if(e instanceof Error)throw"QuotaExceededError"===e.name&&await async function(){for(const e of g)await e()}(),e}for(const e of this.iterateCallbacks("cacheDidUpdate"))await e({cacheName:o,oldResponse:f,newResponse:c.clone(),request:r,event:this.event});return!0}async getCacheKey(e,t){const s=`${e.url} | ${t}`;if(!this._cacheKeys[s]){let a=e;for(const e of this.iterateCallbacks("cacheKeyWillBeUsed"))a=y(await e({mode:t,request:a,event:this.event,params:this.params}));this._cacheKeys[s]=a}return this._cacheKeys[s]}hasCallback(e){for(const t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(const s of this.iterateCallbacks(e))await s(t)}*iterateCallbacks(e){for(const t of this._strategy.plugins)if("function"==typeof t[e]){const s=this._pluginStateMap.get(t),a=a=>{const n=Object.assign(Object.assign({},a),{state:s});return t[e](n)};yield a}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){let e;for(;e=this._extendLifetimePromises.shift();)await e}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,s=!1;for(const e of this.iterateCallbacks("cacheWillUpdate"))if(t=await e({request:this.request,response:t,event:this.event})||void 0,s=!0,!t)break;return s||t&&200!==t.status&&(t=void 0),t}}class m extends class{constructor(e={}){this.cacheName=i(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){const[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});const t=e.event,s="string"==typeof e.request?new Request(e.request):e.request,a="params"in e?e.params:void 0,n=new w(this,{event:t,request:s,params:a}),r=this._getResponse(n,s,t);return[r,this._awaitComplete(r,n,s,t)]}async _getResponse(e,s,a){let n;await e.runCallbacks("handlerWillStart",{event:a,request:s});try{if(n=await this._handle(s,e),!n||"error"===n.type)throw new t("no-response",{url:s.url})}catch(t){if(t instanceof Error)for(const r of e.iterateCallbacks("handlerDidError"))if(n=await r({error:t,event:a,request:s}),n)break;if(!n)throw t}for(const t of e.iterateCallbacks("handlerWillRespond"))n=await t({event:a,request:s,response:n});return n}async _awaitComplete(e,t,s,a){let n,r;try{n=await e}catch(r){}try{await t.runCallbacks("handlerDidRespond",{event:a,request:s,response:n}),await t.doneWaiting()}catch(e){e instanceof Error&&(r=e)}if(await t.runCallbacks("handlerDidComplete",{event:a,request:s,response:n,error:r}),t.destroy(),r)throw r}}{constructor(e={}){e.cacheName=r(e.cacheName),super(e),this._fallbackToNetwork=!1!==e.fallbackToNetwork,this.plugins.push(m.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){const s=await t.cacheMatch(e);return s||(t.event&&"install"===t.event.type?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,s){let a;const n=s.params||{};if(!this._fallbackToNetwork)throw new t("missing-precache-entry",{cacheName:this.cacheName,url:e.url});{0;const t=n.integrity,r=e.integrity,i=!r||r===t;if(a=await s.fetch(new Request(e,{integrity:r||t})),t&&i){this._useDefaultCacheabilityPluginIfNeeded();await s.cachePut(e,a.clone());0}}return a}async _handleInstall(e,s){this._useDefaultCacheabilityPluginIfNeeded();const a=await s.fetch(e);if(!await s.cachePut(e,a.clone()))throw new t("bad-precaching-response",{url:e.url,status:a.status});return a}_useDefaultCacheabilityPluginIfNeeded(){let e=null,t=0;for(const[s,a]of this.plugins.entries())a!==m.copyRedirectedCacheableResponsesPlugin&&(a===m.defaultPrecacheCacheabilityPlugin&&(e=s),a.cacheWillUpdate&&t++);0===t?this.plugins.push(m.defaultPrecacheCacheabilityPlugin):t>1&&null!==e&&this.plugins.splice(e,1)}}m.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:e})=>!e||e.status>=400?null:e},m.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:e})=>e.redirected?await f(e):e};class _{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:s=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new m({cacheName:r(e),plugins:[...t,new l({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this._installAndActiveListenersAdded=!0)}addToCacheList(e){const s=[];for(const a of e){"string"==typeof a?s.push(a):a&&void 0===a.revision&&s.push(a.url);const{cacheKey:e,url:n}=o(a),r="string"!=typeof a&&a.revision?"reload":"default";if(this._urlsToCacheKeys.has(n)&&this._urlsToCacheKeys.get(n)!==e)throw new t("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(n),secondEntry:e});if("string"!=typeof a&&a.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==a.integrity)throw new t("add-to-cache-list-conflicting-integrities",{url:n});this._cacheKeysToIntegrities.set(e,a.integrity)}if(this._urlsToCacheKeys.set(n,e),this._urlsToCacheModes.set(n,r),s.length>0){const e=`Workbox is precaching URLs without revision info: ${s.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return c(e,(async()=>{const t=new h;this.strategy.plugins.push(t);for(const[t,s]of this._urlsToCacheKeys){const a=this._cacheKeysToIntegrities.get(s),n=this._urlsToCacheModes.get(t),r=new Request(t,{integrity:a,cache:n,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:e}))}const{updatedURLs:s,notUpdatedURLs:a}=t;return{updatedURLs:s,notUpdatedURLs:a}}))}activate(e){return c(e,(async()=>{const e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),s=new Set(this._urlsToCacheKeys.values()),a=[];for(const n of t)s.has(n.url)||(await e.delete(n),a.push(n.url));return{deletedURLs:a}}))}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(e){const s=this.getCacheKeyForURL(e);if(!s)throw new t("non-precached-url",{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:s},t.params),this.strategy.handle(t))}}let R;const v=()=>(R||(R=new _),R);s(80);const C=e=>e&&"object"==typeof e?e:{handle:e};class b{constructor(e,t,s="GET"){this.handler=C(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=C(e)}}class q extends b{constructor(e,t,s){super((({url:t})=>{const s=e.exec(t.href);if(s&&(t.origin===location.origin||0===s.index))return s.slice(1)}),t,s)}}class U{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&"CACHE_URLS"===e.data.type){const{payload:t}=e.data;0;const s=Promise.all(t.urlsToCache.map((t=>{"string"==typeof t&&(t=[t]);const s=new Request(...t);return this.handleRequest({request:s,event:e})})));e.waitUntil(s),e.ports&&e.ports[0]&&s.then((()=>e.ports[0].postMessage(!0)))}}))}handleRequest({request:e,event:t}){const s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return void 0;const a=s.origin===location.origin,{params:n,route:r}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:s});let i=r&&r.handler;const c=e.method;if(!i&&this._defaultHandlerMap.has(c)&&(i=this._defaultHandlerMap.get(c)),!i)return void 0;let o;try{o=i.handle({url:s,request:e,event:t,params:n})}catch(e){o=Promise.reject(e)}const h=r&&r.catchHandler;return o instanceof Promise&&(this._catchHandler||h)&&(o=o.catch((async a=>{if(h){0;try{return await h.handle({url:s,request:e,event:t,params:n})}catch(e){e instanceof Error&&(a=e)}}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw a}))),o}findMatchingRoute({url:e,sameOrigin:t,request:s,event:a}){const n=this._routes.get(s.method)||[];for(const r of n){let n;const i=r.match({url:e,sameOrigin:t,request:s,event:a});if(i)return n=i,(Array.isArray(n)&&0===n.length||i.constructor===Object&&0===Object.keys(i).length||"boolean"==typeof i)&&(n=void 0),{route:r,params:n}}return{}}setDefaultHandler(e,t="GET"){this._defaultHandlerMap.set(t,C(e))}setCatchHandler(e){this._catchHandler=C(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t("unregister-route-but-not-found-with-method",{method:e.method});const s=this._routes.get(e.method).indexOf(e);if(!(s>-1))throw new t("unregister-route-route-not-registered");this._routes.get(e.method).splice(s,1)}}let L;class k extends b{constructor(e,t){super((({request:s})=>{const a=e.getURLsToCacheKeys();for(const n of function*(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:a=!0,urlManipulation:n}={}){const r=new URL(e,location.href);r.hash="",yield r.href;const i=function(e,t=[]){for(const s of[...e.searchParams.keys()])t.some((e=>e.test(s)))&&e.searchParams.delete(s);return e}(r,t);if(yield i.href,s&&i.pathname.endsWith("/")){const e=new URL(i.href);e.pathname+=s,yield e.href}if(a){const e=new URL(i.href);e.pathname+=".html",yield e.href}if(n){const e=n({url:r});for(const t of e)yield t.href}}(s.url,t)){const t=a.get(n);if(t){return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}}}),e.strategy)}}function K(e){const s=v();!function(e,s,a){let n;if("string"==typeof e){const t=new URL(e,location.href);n=new b((({url:e})=>e.href===t.href),s,a)}else if(e instanceof RegExp)n=new q(e,s,a);else if("function"==typeof e)n=new b(e,s,a);else{if(!(e instanceof b))throw new t("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});n=e}(L||(L=new U,L.addFetchListener(),L.addCacheListener()),L).registerRoute(n)}(new k(s,e))}var T;(function(e){v().precache(e)})([{'revision':null,'url':'/build/admin.5dc0eea7.css'},{'revision':null,'url':'/build/admin.cee4d78d.js'},{'revision':'ad79405d542b397996a3079203114b42','url':'/build/admin.cee4d78d.js.LICENSE.txt'},{'revision':null,'url':'/build/app.9c351632.css'},{'revision':null,'url':'/build/app.a8f5cd0d.js'},{'revision':'4ff7361e3c3e359ae88c5fae5738746d','url':'/build/app.a8f5cd0d.js.LICENSE.txt'},{'revision':null,'url':'/build/fonts/fa-brands-400.3dc44d22.woff2'},{'revision':null,'url':'/build/fonts/fa-regular-400.3dc6ca01.woff2'},{'revision':null,'url':'/build/fonts/fa-solid-900.496d5fc1.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-300.d2c7d5c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600.17c0392c.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600italic.cc34c6e7.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-700.ed37bc60.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-900.476756cd.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-italic.a07cb9c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-regular.f74389bd.woff2'},{'revision':null,'url':'/build/images/fa-brands-400.05d20183.svg'},{'revision':null,'url':'/build/images/fa-regular-400.9a0810d6.svg'},{'revision':null,'url':'/build/images/fa-solid-900.a838c42a.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-300.4e7fe004.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600.cf2758ae.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600italic.7249d863.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-700.3e4b9e19.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-900.060d8c51.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-italic.08dc9b1c.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-regular.3bb9538c.svg'},{'revision':null,'url':'/build/runtime.6ad5c9da.js'}]),K(T),self.addEventListener("fetch",(function(e){e.respondWith(caches.match(e.request).then((function(t){return t||fetch(e.request)})).catch((function(){return caches.match("/offline.html")})))}))})()})();
\ No newline at end of file
(()=>{"use strict";var e={913:()=>{try{self["workbox:core:6.5.1"]&&_()}catch(e){}},977:()=>{try{self["workbox:precaching:6.5.1"]&&_()}catch(e){}},80:()=>{try{self["workbox:routing:6.5.1"]&&_()}catch(e){}},873:()=>{try{self["workbox:strategies:6.5.1"]&&_()}catch(e){}}},t={};function s(a){var n=t[a];if(void 0!==n)return n.exports;var r=t[a]={exports:{}};return e[a](r,r.exports,s),r.exports}(()=>{s(913);const e=(e,...t)=>{let s=e;return t.length>0&&(s+=` :: ${JSON.stringify(t)}`),s};class t extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}const a={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},n=e=>[a.prefix,e,a.suffix].filter((e=>e&&e.length>0)).join("-"),r=e=>e||n(a.precache),i=e=>e||n(a.runtime);function c(e,t){const s=t();return e.waitUntil(s),s}s(977);function o(e){if(!e)throw new t("add-to-cache-list-unexpected-type",{entry:e});if("string"==typeof e){const t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}const{revision:s,url:a}=e;if(!a)throw new t("add-to-cache-list-unexpected-type",{entry:e});if(!s){const e=new URL(a,location.href);return{cacheKey:e.href,url:e.href}}const n=new URL(a,location.href),r=new URL(a,location.href);return n.searchParams.set("__WB_REVISION__",s),{cacheKey:n.href,url:r.href}}class h{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:s})=>{if("install"===e.type&&t&&t.originalRequest&&t.originalRequest instanceof Request){const e=t.originalRequest.url;s?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return s}}}class l{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{const s=(null==t?void 0:t.cacheKey)||this._precacheController.getCacheKeyForURL(e.url);return s?new Request(s,{headers:e.headers}):e},this._precacheController=e}}let u;async function f(e,s){let a=null;if(e.url){a=new URL(e.url).origin}if(a!==self.location.origin)throw new t("cross-origin-copy-response",{origin:a});const n=e.clone(),r={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},i=s?s(r):r,c=function(){if(void 0===u){const e=new Response("");if("body"in e)try{new Response(e.body),u=!0}catch(e){u=!1}u=!1}return u}()?n.body:await n.blob();return new Response(c,i)}function d(e,t){const s=new URL(e);for(const e of t)s.searchParams.delete(e);return s.href}class p{constructor(){this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t}))}}const g=new Set;s(873);function y(e){return"string"==typeof e?new Request(e):e}class w{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new p,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(const e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){const{event:s}=this;let a=y(e);if("navigate"===a.mode&&s instanceof FetchEvent&&s.preloadResponse){const e=await s.preloadResponse;if(e)return e}const n=this.hasCallback("fetchDidFail")?a.clone():null;try{for(const e of this.iterateCallbacks("requestWillFetch"))a=await e({request:a.clone(),event:s})}catch(e){if(e instanceof Error)throw new t("plugin-error-request-will-fetch",{thrownErrorMessage:e.message})}const r=a.clone();try{let e;e=await fetch(a,"navigate"===a.mode?void 0:this._strategy.fetchOptions);for(const t of this.iterateCallbacks("fetchDidSucceed"))e=await t({event:s,request:r,response:e});return e}catch(e){throw n&&await this.runCallbacks("fetchDidFail",{error:e,event:s,originalRequest:n.clone(),request:r.clone()}),e}}async fetchAndCachePut(e){const t=await this.fetch(e),s=t.clone();return this.waitUntil(this.cachePut(e,s)),t}async cacheMatch(e){const t=y(e);let s;const{cacheName:a,matchOptions:n}=this._strategy,r=await this.getCacheKey(t,"read"),i=Object.assign(Object.assign({},n),{cacheName:a});s=await caches.match(r,i);for(const e of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await e({cacheName:a,matchOptions:n,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(e,s){const a=y(e);var n;await(n=0,new Promise((e=>setTimeout(e,n))));const r=await this.getCacheKey(a,"write");if(!s)throw new t("cache-put-with-no-response",{url:(i=r.url,new URL(String(i),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var i;const c=await this._ensureResponseSafeToCache(s);if(!c)return!1;const{cacheName:o,matchOptions:h}=this._strategy,l=await self.caches.open(o),u=this.hasCallback("cacheDidUpdate"),f=u?await async function(e,t,s,a){const n=d(t.url,s);if(t.url===n)return e.match(t,a);const r=Object.assign(Object.assign({},a),{ignoreSearch:!0}),i=await e.keys(t,r);for(const t of i)if(n===d(t.url,s))return e.match(t,a)}(l,r.clone(),["__WB_REVISION__"],h):null;try{await l.put(r,u?c.clone():c)}catch(e){if(e instanceof Error)throw"QuotaExceededError"===e.name&&await async function(){for(const e of g)await e()}(),e}for(const e of this.iterateCallbacks("cacheDidUpdate"))await e({cacheName:o,oldResponse:f,newResponse:c.clone(),request:r,event:this.event});return!0}async getCacheKey(e,t){const s=`${e.url} | ${t}`;if(!this._cacheKeys[s]){let a=e;for(const e of this.iterateCallbacks("cacheKeyWillBeUsed"))a=y(await e({mode:t,request:a,event:this.event,params:this.params}));this._cacheKeys[s]=a}return this._cacheKeys[s]}hasCallback(e){for(const t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(const s of this.iterateCallbacks(e))await s(t)}*iterateCallbacks(e){for(const t of this._strategy.plugins)if("function"==typeof t[e]){const s=this._pluginStateMap.get(t),a=a=>{const n=Object.assign(Object.assign({},a),{state:s});return t[e](n)};yield a}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){let e;for(;e=this._extendLifetimePromises.shift();)await e}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,s=!1;for(const e of this.iterateCallbacks("cacheWillUpdate"))if(t=await e({request:this.request,response:t,event:this.event})||void 0,s=!0,!t)break;return s||t&&200!==t.status&&(t=void 0),t}}class m extends class{constructor(e={}){this.cacheName=i(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){const[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});const t=e.event,s="string"==typeof e.request?new Request(e.request):e.request,a="params"in e?e.params:void 0,n=new w(this,{event:t,request:s,params:a}),r=this._getResponse(n,s,t);return[r,this._awaitComplete(r,n,s,t)]}async _getResponse(e,s,a){let n;await e.runCallbacks("handlerWillStart",{event:a,request:s});try{if(n=await this._handle(s,e),!n||"error"===n.type)throw new t("no-response",{url:s.url})}catch(t){if(t instanceof Error)for(const r of e.iterateCallbacks("handlerDidError"))if(n=await r({error:t,event:a,request:s}),n)break;if(!n)throw t}for(const t of e.iterateCallbacks("handlerWillRespond"))n=await t({event:a,request:s,response:n});return n}async _awaitComplete(e,t,s,a){let n,r;try{n=await e}catch(r){}try{await t.runCallbacks("handlerDidRespond",{event:a,request:s,response:n}),await t.doneWaiting()}catch(e){e instanceof Error&&(r=e)}if(await t.runCallbacks("handlerDidComplete",{event:a,request:s,response:n,error:r}),t.destroy(),r)throw r}}{constructor(e={}){e.cacheName=r(e.cacheName),super(e),this._fallbackToNetwork=!1!==e.fallbackToNetwork,this.plugins.push(m.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){const s=await t.cacheMatch(e);return s||(t.event&&"install"===t.event.type?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,s){let a;const n=s.params||{};if(!this._fallbackToNetwork)throw new t("missing-precache-entry",{cacheName:this.cacheName,url:e.url});{0;const t=n.integrity,r=e.integrity,i=!r||r===t;if(a=await s.fetch(new Request(e,{integrity:r||t})),t&&i){this._useDefaultCacheabilityPluginIfNeeded();await s.cachePut(e,a.clone());0}}return a}async _handleInstall(e,s){this._useDefaultCacheabilityPluginIfNeeded();const a=await s.fetch(e);if(!await s.cachePut(e,a.clone()))throw new t("bad-precaching-response",{url:e.url,status:a.status});return a}_useDefaultCacheabilityPluginIfNeeded(){let e=null,t=0;for(const[s,a]of this.plugins.entries())a!==m.copyRedirectedCacheableResponsesPlugin&&(a===m.defaultPrecacheCacheabilityPlugin&&(e=s),a.cacheWillUpdate&&t++);0===t?this.plugins.push(m.defaultPrecacheCacheabilityPlugin):t>1&&null!==e&&this.plugins.splice(e,1)}}m.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:e})=>!e||e.status>=400?null:e},m.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:e})=>e.redirected?await f(e):e};class _{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:s=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new m({cacheName:r(e),plugins:[...t,new l({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this._installAndActiveListenersAdded=!0)}addToCacheList(e){const s=[];for(const a of e){"string"==typeof a?s.push(a):a&&void 0===a.revision&&s.push(a.url);const{cacheKey:e,url:n}=o(a),r="string"!=typeof a&&a.revision?"reload":"default";if(this._urlsToCacheKeys.has(n)&&this._urlsToCacheKeys.get(n)!==e)throw new t("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(n),secondEntry:e});if("string"!=typeof a&&a.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==a.integrity)throw new t("add-to-cache-list-conflicting-integrities",{url:n});this._cacheKeysToIntegrities.set(e,a.integrity)}if(this._urlsToCacheKeys.set(n,e),this._urlsToCacheModes.set(n,r),s.length>0){const e=`Workbox is precaching URLs without revision info: ${s.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return c(e,(async()=>{const t=new h;this.strategy.plugins.push(t);for(const[t,s]of this._urlsToCacheKeys){const a=this._cacheKeysToIntegrities.get(s),n=this._urlsToCacheModes.get(t),r=new Request(t,{integrity:a,cache:n,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:e}))}const{updatedURLs:s,notUpdatedURLs:a}=t;return{updatedURLs:s,notUpdatedURLs:a}}))}activate(e){return c(e,(async()=>{const e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),s=new Set(this._urlsToCacheKeys.values()),a=[];for(const n of t)s.has(n.url)||(await e.delete(n),a.push(n.url));return{deletedURLs:a}}))}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(e){const s=this.getCacheKeyForURL(e);if(!s)throw new t("non-precached-url",{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:s},t.params),this.strategy.handle(t))}}let R;const v=()=>(R||(R=new _),R);s(80);const C=e=>e&&"object"==typeof e?e:{handle:e};class b{constructor(e,t,s="GET"){this.handler=C(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=C(e)}}class q extends b{constructor(e,t,s){super((({url:t})=>{const s=e.exec(t.href);if(s&&(t.origin===location.origin||0===s.index))return s.slice(1)}),t,s)}}class U{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&"CACHE_URLS"===e.data.type){const{payload:t}=e.data;0;const s=Promise.all(t.urlsToCache.map((t=>{"string"==typeof t&&(t=[t]);const s=new Request(...t);return this.handleRequest({request:s,event:e})})));e.waitUntil(s),e.ports&&e.ports[0]&&s.then((()=>e.ports[0].postMessage(!0)))}}))}handleRequest({request:e,event:t}){const s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return void 0;const a=s.origin===location.origin,{params:n,route:r}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:s});let i=r&&r.handler;const c=e.method;if(!i&&this._defaultHandlerMap.has(c)&&(i=this._defaultHandlerMap.get(c)),!i)return void 0;let o;try{o=i.handle({url:s,request:e,event:t,params:n})}catch(e){o=Promise.reject(e)}const h=r&&r.catchHandler;return o instanceof Promise&&(this._catchHandler||h)&&(o=o.catch((async a=>{if(h){0;try{return await h.handle({url:s,request:e,event:t,params:n})}catch(e){e instanceof Error&&(a=e)}}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw a}))),o}findMatchingRoute({url:e,sameOrigin:t,request:s,event:a}){const n=this._routes.get(s.method)||[];for(const r of n){let n;const i=r.match({url:e,sameOrigin:t,request:s,event:a});if(i)return n=i,(Array.isArray(n)&&0===n.length||i.constructor===Object&&0===Object.keys(i).length||"boolean"==typeof i)&&(n=void 0),{route:r,params:n}}return{}}setDefaultHandler(e,t="GET"){this._defaultHandlerMap.set(t,C(e))}setCatchHandler(e){this._catchHandler=C(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t("unregister-route-but-not-found-with-method",{method:e.method});const s=this._routes.get(e.method).indexOf(e);if(!(s>-1))throw new t("unregister-route-route-not-registered");this._routes.get(e.method).splice(s,1)}}let L;class k extends b{constructor(e,t){super((({request:s})=>{const a=e.getURLsToCacheKeys();for(const n of function*(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:a=!0,urlManipulation:n}={}){const r=new URL(e,location.href);r.hash="",yield r.href;const i=function(e,t=[]){for(const s of[...e.searchParams.keys()])t.some((e=>e.test(s)))&&e.searchParams.delete(s);return e}(r,t);if(yield i.href,s&&i.pathname.endsWith("/")){const e=new URL(i.href);e.pathname+=s,yield e.href}if(a){const e=new URL(i.href);e.pathname+=".html",yield e.href}if(n){const e=n({url:r});for(const t of e)yield t.href}}(s.url,t)){const t=a.get(n);if(t){return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}}}),e.strategy)}}function K(e){const s=v();!function(e,s,a){let n;if("string"==typeof e){const t=new URL(e,location.href);n=new b((({url:e})=>e.href===t.href),s,a)}else if(e instanceof RegExp)n=new q(e,s,a);else if("function"==typeof e)n=new b(e,s,a);else{if(!(e instanceof b))throw new t("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});n=e}(L||(L=new U,L.addFetchListener(),L.addCacheListener()),L).registerRoute(n)}(new k(s,e))}var T;(function(e){v().precache(e)})([{'revision':null,'url':'/build/admin.3edfb527.js'},{'revision':'ad79405d542b397996a3079203114b42','url':'/build/admin.3edfb527.js.LICENSE.txt'},{'revision':null,'url':'/build/admin.4de55830.css'},{'revision':null,'url':'/build/app.3644f7b2.js'},{'revision':'4ff7361e3c3e359ae88c5fae5738746d','url':'/build/app.3644f7b2.js.LICENSE.txt'},{'revision':null,'url':'/build/app.8a3f698b.css'},{'revision':null,'url':'/build/fonts/fa-brands-400.3dc44d22.woff2'},{'revision':null,'url':'/build/fonts/fa-regular-400.3dc6ca01.woff2'},{'revision':null,'url':'/build/fonts/fa-solid-900.496d5fc1.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-300.d2c7d5c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600.17c0392c.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600italic.cc34c6e7.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-700.ed37bc60.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-900.476756cd.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-italic.a07cb9c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-regular.f74389bd.woff2'},{'revision':null,'url':'/build/images/fa-brands-400.05d20183.svg'},{'revision':null,'url':'/build/images/fa-regular-400.9a0810d6.svg'},{'revision':null,'url':'/build/images/fa-solid-900.a838c42a.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-300.4e7fe004.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600.cf2758ae.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600italic.7249d863.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-700.3e4b9e19.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-900.060d8c51.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-italic.08dc9b1c.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-regular.3bb9538c.svg'},{'revision':null,'url':'/build/runtime.6ad5c9da.js'}]),K(T),self.addEventListener("fetch",(function(e){e.respondWith(caches.match(e.request).then((function(t){return t||fetch(e.request)})).catch((function(){return caches.match("/offline.html")})))}))})()})();
\ No newline at end of file
......@@ -27,9 +27,17 @@ class AchatMonnaieAdmin extends FluxAdmin
public function createQuery($context = 'list')
{
$query = parent::createQuery($context);
//In mode ssa_friendly_flux_type_names, we display on this page all cotisation
//initial operation (i.e. we display vente_emlc operations in addition to achat_monnaie)
if($this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names')) {
$query->andWhere($query->getRootAliases()[0] . '.parenttype = :parenttype' . ' OR ' . $query->getRootAliases()[0] . '.parenttype = :parenttype2')
->setParameter('parenttype', Flux::TYPE_ACHAT)
->setParameter('parenttype2', Flux::TYPE_VENTE_EMLC);
} else {
$query->andWhere($query->getRootAliases()[0] . '.parenttype = :parenttype')
->setParameter('parenttype', Flux::TYPE_ACHAT);
}
return $query;
}
......@@ -43,6 +51,12 @@ class AchatMonnaieAdmin extends FluxAdmin
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper): void
{
//In mode ssa_friendly_flux_type_names, we display on this page all cotisation
//initial operation (i.e. we display vente_emlc operations in addition to achat_monnaie)
//therefore filtering by type achat_monnaie_adherent VS achat_monnaie_prestataire
//would be confusing (selecting one of them would hide all vente_emlc operations)
if(!$this->getConfigurationPool()->getContainer()->getParameter('tav_env')
|| !$this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names')) {
$datagridMapper->add('type', null, [
'advanced_filter' => false,
'show_filter' => true,
......@@ -55,6 +69,7 @@ class AchatMonnaieAdmin extends FluxAdmin
],
]);
}
}
/**
* {@inheritdoc}
......@@ -81,12 +96,24 @@ class AchatMonnaieAdmin extends FluxAdmin
$datagrid->buildPager();
$query = clone $datagrid->getQuery();
if($this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names')) {
$query
->select('SUM( ' . $query->getRootAlias() . '.montant) as total')
->andWhere($query->getRootAlias() . '.parenttype = :parenttype' . ' OR ' . $query->getRootAliases()[0] . '.parenttype = :parenttype2')
->setParameter('parenttype', Flux::TYPE_ACHAT)
->setParameter('parenttype2', Flux::TYPE_VENTE_EMLC)
->setFirstResult(null)
->setMaxResults(null);
} else {
$query
->select('SUM( ' . $query->getRootAlias() . '.montant) as total')
->andWhere($query->getRootAlias() . '.parenttype = :parenttype')
->setParameter('parenttype', Flux::TYPE_ACHAT)
->setFirstResult(null)
->setMaxResults(null);
}
$result = $query->execute([], \Doctrine\ORM\Query::HYDRATE_SINGLE_SCALAR);
......
......@@ -5,6 +5,7 @@ namespace App\Admin;
use App\Entity\AccountAdherent;
use App\Entity\Adherent;
use App\Entity\CotisationAdherent;
use App\Entity\DependentChild;
use App\Entity\Geoloc;
use App\Entity\GlobalParameter;
use App\Entity\Groupe;
......@@ -14,8 +15,10 @@ use App\Entity\ProfilDeCotisation;
use App\Enum\CurrencyEnum;
use App\Events\MLCEvents;
use App\Exporter\CustomDoctrineORMQuerySourceIterator;
use App\Form\Type\DependentChildFormType;
use App\Form\Type\GeolocFormType;
use App\Form\Type\UserFormType;
use App\Utils\TAVCotisationUtils;
use Doctrine\ORM\Query;
use FOS\UserBundle\Event\UserEvent;
use Knp\Menu\ItemInterface as MenuItemInterface;
......@@ -35,12 +38,17 @@ use Sonata\UserBundle\Model\UserManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
/**
* Administration des adhérents.
......@@ -54,6 +62,7 @@ class AdherentAdmin extends AbstractAdmin
protected $baseRouteName = 'adherent';
protected $baseRoutePattern = 'adherent';
protected $security;
protected $tavCotisationUtils;
protected $datagridValues = [
// reverse order (default = 'ASC')
......@@ -69,6 +78,11 @@ class AdherentAdmin extends AbstractAdmin
$this->security = $security;
}
public function setTavCotisationUtils(TAVCotisationUtils $tavCotisationUtils)
{
$this->tavCotisationUtils = $tavCotisationUtils;
}
public function configure()
{
parent::configure();
......@@ -144,6 +158,11 @@ class AdherentAdmin extends AbstractAdmin
if (null == $adherent->getGeoloc()) {
$adherent->setGeoloc(new Geoloc());
}
// params
$tav_env = $this->getConfigurationPool()->getContainer()->getParameter('tav_env');
$household_based_allowance = $this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance');
$formMapper
->tab('General')
->with('Identité', ['class' => 'col-md-7'])
......@@ -157,6 +176,8 @@ class AdherentAdmin extends AbstractAdmin
'required' => true,
'with_geoloc' => false,
'with_latlon' => false,
'with_subterritory' => $tav_env && $household_based_allowance,
'with_quartier' => $tav_env && $household_based_allowance
])
->end()
->with('Groupe', ['class' => 'col-md-5'])
......@@ -171,7 +192,70 @@ class AdherentAdmin extends AbstractAdmin
->end()
;
if ($this->getConfigurationPool()->getContainer()->getParameter('tav_env')) {
/**
* In TAV env, 2 allowance processes possible:
* - household based (if param set)
* - cotisation profile and rate based (default)
*/
if ($tav_env) {
if ($household_based_allowance) {
$formMapper
->tab('General')
->with('Foyer', ['class' => 'col-md-7'])
->add('householdComposition',ChoiceType::class, [
'choices' => [
"Personne seule" => "Personne seule",
"Couple sans enfant à charge" => "Couple sans enfant à charge",
"Famille mono-parentale" => "Famille mono-parentale",
"Couple avec enfant(s) à charge" => "Couple avec enfant(s) à charge",
"Autre" => "Autre"
],
'label' => "Composition du foyer (pour information)",
'required' => true,
'placeholder' => "Choix de la composition du foyer",
])
->add('householdAdultCount',IntegerType::class, [
'label' => "Nombre total d'adultes dans le foyer (pour calculer l'allocation)",
'constraints' => [
new GreaterThanOrEqual(['value' => 0]),
],
'required' => true,
'attr' => [
'autocomplete' => false
]
])
->add('dependentChildren', CollectionType::class, [
'entry_type' => DependentChildFormType::class,
'entry_options' => [
'label' => true,
'data_class' => DependentChild::class,
'attr' => ['class' => 'border pl-3 pr-3 pt-2']
],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => "Enfant(s) à charge (pour calculer l'allocation)"
])
->end()
->end();
// Add cotisation info
$formMapper
->tab('General')
->with('Informations de cotisation', ['class' => 'col-md-5'])
->add('cotisationAmount', NumberType::class, [
'label' => 'Montant de la cotisation (en €)',
'help' => 'Montant minimum : 10€ par foyer + 5€/personne supplémentaire du foyer'
])
->add('allocationAmount', NumberType::class, [
'label' => 'Montant d\'allocation prévu en fonction du foyer (en MonA)',
'disabled' => true,
'required' => false,
'help' => 'Le montant de l\'allocation sera calculé automatiquement en fonction des informations du foyer une fois les informations sauvegardées.'
])
->end()
->end();
} else {
// For Comptoir role in edit mode, hide profile choice
$displayProfilChoice = true;
$isComptoirOnly =
......@@ -200,7 +284,10 @@ class AdherentAdmin extends AbstractAdmin
}
return $choice->__toString();
}
},
'attr' => [
'autocomplete' => 'off'
]
])
->end()
->end();
......@@ -219,28 +306,41 @@ class AdherentAdmin extends AbstractAdmin
"prélèvement" => "prélèvement"
],
'empty_data' => null,
'placeholder' => 'Choisir un moyen de paiement'
'placeholder' => 'Choisir un moyen de paiement',
'attr' => [
'autocomplete' => 'off'
]
])
->add('jourPrelevement', ChoiceType::class, [
'required' => false,
'label' => 'Jour de prélèvement :',
'choices' => $this->daysOfMonth(),
'empty_data' => null,
'placeholder' => 'Choisir un jour de prélèvement'
'placeholder' => 'Choisir un jour de prélèvement',
'attr' => [
'autocomplete' => 'off'
]
])
->add('mailRappelCotisation', CheckboxType::class, [
'required' => false,
'label' => 'Recevoir un rappel du paiement de ma cotisation par mail',
'attr' => [
'autocomplete' => 'off'
]
])
->add('jourMailRappelCotisation', ChoiceType::class, [
'required' => false,
'label' => 'Jour de l\'envoi du mail de rappel :',
'choices' => $this->daysOfMonth(),
'empty_data' => null,
'placeholder' => 'Choisir un jour pour l\'envoi du mail de rappel'
'placeholder' => 'Choisir un jour pour l\'envoi du mail de rappel',
'attr' => [
'autocomplete' => 'off'
]
])
->end()
->end();
}
if (!empty($adherent) && !empty($adherent->getEmlcAccount()) ) {
$em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
......@@ -253,16 +353,57 @@ class AdherentAdmin extends AbstractAdmin
'disabled' => true,
'required' => false,
'label' => 'Solde e-' . $mlc . ' :',
'data' => $balance . ' ' . $mlc
'data' => $balance . ' ' . $mlc,
'attr' => [
'autocomplete' => 'off'
]
])
->end()
->end();
//Add form part allowing super admin to fix balance
if($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')
&& $this->security->isGranted('ROLE_SUPER_ADMIN')) {
$formMapper
->tab('General')
->with('Informations de cotisation')
->add('fixedBalance', TextType::class, [
'label' => "Corriger le solde suite à une erreur de cotisation",
'mapped' => false,
'required' => false,
'attr' => [
'autocomplete' => 'off',
'class' => 'fixBalanceAdherentFormPart'
]
])
->add('justification', TextType::class, [
'mapped' => false,
'required' => false,
'attr' => [
'autocomplete' => 'off',
'class' => 'fixBalanceAdherentFormPart'
]
])
->add('password', PasswordType::class, [
'label' => 'Mot de passe pour corriger le solde',
'mapped' => false,
'required' => false,
'data' => "",
'attr' => [
'autocomplete' => 'off',
'class' => 'fixBalanceAdherentFormPart'
]
])
->end()
->end();
}
}
}
$em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
$formMapper->getFormBuilder()->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($em) {
$adherent = $event->getData();
// Check user email
$user = $adherent->getUser();
if (!$user || null === $user->getId()) {
$repo = $em->getRepository(User::class);
......@@ -273,6 +414,37 @@ class AdherentAdmin extends AbstractAdmin
$user->setUsername($user->getEmail());
}
}
if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) {
// check cotisation amount
$adultsCount = $adherent->getHouseholdAdultCount();
$dependentChildrenCount = count($adherent->getDependentChildren());
$minCotisationAmount = 10 + 5 * ( $adultsCount - 1 ) + 5 * $dependentChildrenCount;
if ($adherent->getCotisationAmount() < $minCotisationAmount) {
$event->getForm()->get('cotisationAmount')->addError(new FormError('Le montant minimum est de ' . $minCotisationAmount . '€ (selon les données du foyer indiquées)'));
}
// try to fix balance if required
if($this->security->isGranted('ROLE_SUPER_ADMIN')
&& $event->getForm()->has('fixedBalance')
&& $event->getForm()->get('fixedBalance')->getData()
&& $event->getForm()->get('fixedBalance')->getData() >= 0) {
$password = $this->getConfigurationPool()->getContainer()->getParameter('fix_balance_adherent_password');
//this password purpose is to be an additional warning for super admin and
//is not intended to be securely stored as only super admin can use this feature
if ($event->getForm()->get('password')->getData() !== $password) {
$event->getForm()->get('password')->addError(new FormError('Mot de passe incorrect.'));
} elseif(!$event->getForm()->get('justification')->getData()) {
$event->getForm()->get('justification')->addError(new FormError('Merci de justifier cette opération sensible.'));
} else {
$this->tavCotisationUtils->fixBalance(
$adherent, $event->getForm()->get('fixedBalance')->getData(), $event->getForm()->get('justification')->getData()
);
$em->flush();
}
}
}
});
parent::configureFormFields($formMapper);
}
......@@ -314,6 +486,11 @@ class AdherentAdmin extends AbstractAdmin
$adherent->addAccount($account);
$em->persist($account);
}
if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) {
$this->tavCotisationUtils->calculateAllowanceAccordingToHousehold($adherent);
}
$em->persist($adherent->getUser());
$em->persist($adherent);
$em->flush();
......@@ -463,6 +640,11 @@ class AdherentAdmin extends AbstractAdmin
->addIdentifier('user.email', null, ['label' => 'Email'])
;
if ($this->security->isGranted('ROLE_TRESORIER')) {
$actions = [];
} else {
$actions = ['edit' => []];
}
if (!$this->getConfigurationPool()->getContainer()->getParameter('tav_env')) {
$listMapper
->add(
......@@ -483,6 +665,27 @@ class AdherentAdmin extends AbstractAdmin
'template' => '@kohinos/tav/list_user_tav_cotisation.html.twig',
]
);
if(
$this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')
&& (
$this->security->isGranted('ROLE_SUPER_ADMIN')
|| $this->security->isGranted('ROLE_ADMIN_SIEGE')
|| $this->security->isGranted('ROLE_TRESORIER')
)
) {
$listMapper
->add(
'ceiling',
null,
[
'label' => 'Solde & plafond',
'template' => '@kohinos/tav/list_user_ssa_ceiling.html.twig',
]
);
$actions['withdrawDownToTheCeiling'] = [
'template' => '@kohinos/tav/adherent_action_withdraw_down_to_the_ceiling.html.twig'
];
}
}
$listMapper
......@@ -505,7 +708,7 @@ class AdherentAdmin extends AbstractAdmin
'label' => 'Mis à jour le',
])
->add('_action', null, [
'actions' => ['edit' => []],
'actions' => $actions,
])
;
}
......@@ -513,7 +716,9 @@ class AdherentAdmin extends AbstractAdmin
protected function configureRoutes(RouteCollection $collection)
{
parent::configureRoutes($collection);
$collection->remove('delete');
$collection
->remove('delete')
->add('withdrawDownToTheCeiling', $this->getRouterIdParameter() . '/withdrawDownToTheCeiling');
}
public function getBatchActions()
......
......@@ -64,6 +64,8 @@ class FluxAdmin extends AbstractAdmin
protected function configureExportFields(): array
{
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
return [
$this->trans('Date') => 'createdAt',
$this->trans('Expediteur') => 'expediteur',
......@@ -91,10 +93,12 @@ class FluxAdmin extends AbstractAdmin
*/
protected function configureListFields(ListMapper $listMapper)
{
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
unset($this->listModes['mosaic']);
$listMapper
->add('createdAt', 'datetime', ['label' => 'Date'])
->add('type', 'text', ['label' => 'Type', 'template' => '@kohinos/bundles/SonataAdminBundle/Block/translated_value.html.twig'])
->add($ssaFriendlyTypeNames ? 'ssaFriendlyTypeName' : 'type', 'text', ['label' => 'Type', 'template' => '@kohinos/bundles/SonataAdminBundle/Block/translated_value.html.twig'])
->add('montant', 'decimal', ['label' => 'Montant', 'attributes' => ['fraction_digits' => 2]])
->add('expediteur', null, ['label' => 'Expediteur'])
->add('destinataire', null, ['label' => 'Destinataire'])
......@@ -110,6 +114,11 @@ class FluxAdmin extends AbstractAdmin
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper): void
{
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
$choices = $ssaFriendlyTypeNames ? //in ssa mode, remove flux types that are irrelevant or confusing
['Transactions' => Flux::TYPE_TRANSACTION]
: ['Cotisations' => Flux::TYPE_COTISATION, 'Achat de monnaie' => Flux::TYPE_ACHAT, 'Dépôt de billets' => Flux::TYPE_CHANGE, 'Retrait' => Flux::TYPE_RETRAIT, 'Transactions' => Flux::TYPE_TRANSACTION, 'Transferts' => Flux::TYPE_TRANSFERT, 'Vente' => Flux::TYPE_VENTE];
$datagridMapper
->add('transfert_or_transaction', 'doctrine_orm_callback', [
'label' => 'Type',
......@@ -127,17 +136,32 @@ class FluxAdmin extends AbstractAdmin
'show_filter' => true,
'field_type' => SChoiceType::class,
'field_options' => [
'choices' => ['Cotisations' => Flux::TYPE_COTISATION, 'Achat de monnaie' => Flux::TYPE_ACHAT, 'Dépôt de billets' => Flux::TYPE_CHANGE, 'Retrait' => Flux::TYPE_RETRAIT, 'Transactions' => Flux::TYPE_TRANSACTION, 'Transferts' => Flux::TYPE_TRANSFERT, 'Vente' => Flux::TYPE_VENTE],
'choices' => $choices,
'placeholder' => 'Indifférent',
'expanded' => true,
'multiple' => false,
],
])
]);
if ($ssaFriendlyTypeNames) {
$datagridMapper
->add('type', null, [
'label' => 'Type plus précis',
'advanced_filter' => false,
'show_filter' => true,
])
'field_type' => SChoiceType::class,
'field_options' => [
'choices' => array_flip(Flux::ssaUsefulFriendlyTypeNames(true))
]
]);
} else {
$datagridMapper
->add('type', null, [
'label' => 'Type plus précis',
'advanced_filter' => false,
'show_filter' => true,
]);
}
$datagridMapper
->add('operateur', null, [
'label' => 'Operateur',
'advanced_filter' => false,
......@@ -182,9 +206,11 @@ class FluxAdmin extends AbstractAdmin
public function getExportFields()
{
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
return [
'Date' => 'created_at',
'Type' => 'type',
'Type' => $ssaFriendlyTypeNames ? 'ssaFriendlyTypeName' : 'type',
'Montant' => 'montant',
'Expediteur' => 'expediteur',
'Destinataire' => 'destinataire',
......
......@@ -21,6 +21,7 @@ use App\Exporter\CustomDoctrineORMQuerySourceIterator;
use App\Form\Type\ContactEntityFormType;
use App\Form\Type\GeolocPrestataireFormType;
use App\Form\Type\UserFormType;
use App\Form\Type\PrestataireProductFamilyFormType;
use Doctrine\ORM\Query;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use FOS\UserBundle\Event\UserEvent;
......@@ -205,6 +206,11 @@ class PrestataireAdmin extends AbstractAdmin
'label' => 'SIRET :',
'required' => false,
])
//bic is new field in kohinos-ssa (I think it's OK to add it for non-tav env as well)
->add('bic', TextType::class, [
'label' => 'BIC :',
'required' => false,
])
->add('iban', PersonalDataType::class, [
'label' => 'IBAN :',
'required' => false,
......@@ -352,6 +358,52 @@ class PrestataireAdmin extends AbstractAdmin
->end()
->end()
;
/**
* In case of prestataire self init and evaluation use case activated,
* set prestataire disabled by default & add 'conventionnement' field.
*/
if ($this->getConfigurationPool()->getContainer()->getParameter('presta_self_init_and_eval')) {
$formMapper
->tab('Prestataire')
->with('Prestataire', ['class' => 'col-md-6'])
->remove('enabled')
->add('enabled', null, [
'label' => 'Activé ?',
'required' => false,
'data' => $presta && $presta->getRaison() ? $presta->isEnabled() : false, //force false at create time only
'attr' => ['autocomplete' => 'off']
])
->add('conventionnement', ChoiceType::class, [
'choices' => [
'50 %' => '0.50', //using strings (and not floats) seems required
'75 %' => '0.75', //to make display work fine
'100 %' => '1.00',
],
'required' => false,
'attr' => ['autocomplete' => 'off'] //avoid non-saved value to be displayed
])
->end()
->end();
}
if ($this->getConfigurationPool()->getContainer()->getParameter('presta_extra_data')) {
$formMapper
->tab('Prestataire')
->with('Prestataire', ['class' => 'col-md-6'])
->add('prestataireProductFamilies', CollectionType::class, [
'label' => 'Produits que le prestataire a renseigné',
'entry_type' => PrestataireProductFamilyFormType::class,
'required' => false,
'disabled' => true,
'entry_options' => [
'attr' => ['class' => 'prestataire-products-families-row'],
],
])
->end()
->end();
}
// @TODO : add tags model transformer if add new from text
// ->get('etats')
// ->addModelTransformer(new CallbackTransformer(
......@@ -670,6 +722,11 @@ class PrestataireAdmin extends AbstractAdmin
],
],
];
if (($this->security->isGranted('ROLE_GESTION_GROUPE') || $this->security->isGranted('ROLE_SUPER_ADMIN')) && $this->getConfigurationPool()->getContainer()->getParameter('presta_self_init_and_eval')) {
$actions['reviewPrestaQuiz'] = [
'template' => '@kohinos/tav/prestaquiz/review.html.twig'
];
}
if (null != $this->security->getUser() && ($this->security->isGranted('ROLE_SUPER_ADMIN') || $this->security->isGranted('ROLE_ADMIN_PRESTATAIRE_COTISATIONS_ALL') || $this->security->isGranted('ROLE_ADMIN_PRESTATAIRE_COTISATIONS_CREATE'))) {
$actions['addfreecotisationpresta'] = [
'template' => '@kohinos/bundles/SonataAdminBundle/CRUD/list__action_addfreecotisationpresta.html.twig',
......@@ -725,7 +782,6 @@ class PrestataireAdmin extends AbstractAdmin
'label' => 'Cotisation à jour',
'template' => '@kohinos/bundles/SonataAdminBundle/CRUD/list_presta_cotisation.html.twig',
]
)
->add('users', null, [
'label' => 'Gestionnaires [Cotisation à jour]',
......@@ -765,6 +821,7 @@ class PrestataireAdmin extends AbstractAdmin
$collection
->add('addfreecotisationpresta', $this->getRouterIdParameter() . '/addfreecotisationpresta')
->add('addfreecotisationadh', $this->getRouterIdParameter() . '/addfreecotisationadh')
->add('reviewprestaquiz', $this->getRouterIdParameter() . '/reviewprestaquiz')
;
}
......
<?php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
/**
* Administration des familles de produits.
*
* KOHINOS : Outil de gestion de Monnaie Locale Complémentaire
*/
class ProductFamilyAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->add('name', TextType::class, [
'label' => 'Nom'
]);
}
protected function configureDatagridFilters(DatagridMapper $datagrid): void
{
$datagrid->add('name', null, [
'label' => 'Nom'
]);
}
protected function configureListFields(ListMapper $list): void
{
$list->addIdentifier('name', null, [
'label' => 'Nom'
]);
}
}
\ No newline at end of file
......@@ -96,4 +96,8 @@ class ReconversionAdmin extends FluxAdmin
'Reconverti ?' => 'reconverti',
];
}
public function isReconversionAdmin() {
return true;
}
}
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use App\Entity\Reconversion;
use App\Entity\Siege;
use App\Enum\MoyenEnum;
use App\Enum\ReconversionFrequencyEnum;
use App\Utils\CustomEntityManager;
use App\Utils\OperationUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Twig\Environment;
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class ReconversionMonaPrestatairesCommand extends Command
{
//Les reconversions doivent avoir lieu les dimanches soir qui précèdent les 14 et 28.
// On utilise les crons suivants :
//15 23 * * 0 kohinos [ "$(date +\%d)" -ge 08 -a "$(date +\%d)" -le 14 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
//15 23 * * 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
//17 23 * * 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_a_month
//19 23 * 1,3,5,7,9,11 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_every_two_month
protected static $defaultName = 'kohinos:ssa:reconversion-prestataires';
protected $em;
protected $mailer;
protected $templating;
protected $io;
protected $param;
protected $operationUtils;
public function __construct(
CustomEntityManager $em,
\Swift_Mailer $mailer,
Environment $templating,
OperationUtils $operationUtils
) {
$this->em = $em;
$this->mailer = $mailer;
$this->templating = $templating;
$this->operationUtils = $operationUtils;
parent::__construct();
}
protected function configure()
{
$this
->setDescription('SSA : générer des flux de reconversion conformément à la fréquence configurée par les prestataires')
->addArgument('reconvmode', InputArgument::REQUIRED, 'Restriction aux seuls prestataires ayant opté pour la fréquence choisie.');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io = new SymfonyStyle($input, $output);
$reconvmode = $input->getArgument('reconvmode');
if (!in_array($reconvmode, ReconversionFrequencyEnum::getAvailableTypes())) {
$this->io->error("L'argument " . $reconvmode . ' est invalide. Choix possibles : '
. implode(', ', ReconversionFrequencyEnum::getAvailableTypes()));
return 1;
}
$this->io->title('START. Reconversion pour les prestataires de compte positif ayant choisi la fréquence : ' . $reconvmode);
$prestas = $this->em->getRepository(Prestataire::class)->findBy(['reconversionFrequency' => $reconvmode]);
foreach ($prestas as $p) {
$balance = $p->getEmlcAccount()->getBalance();
if ($balance <= 0) {
continue;
}
/* @var Prestataire $p */
$flux = new Reconversion();
//do not fill operator as it is automated process $entity->setOperateur();
$flux->setExpediteur($p);
$flux->setMontant($balance); //montant maximal
$flux->setDestinataire($this->em->getRepository(Siege::class)->getTheOne());
$flux->setMoyen(MoyenEnum::MOYEN_AUTRE);
$flux->setTauxreconversion(
!empty($p->getTauxreconversion()) ?
$p->getTauxreconversion()
: floatval(
$this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::RECONVERSION_PRESTATAIRE)
)
);
$now = new \DateTime();
$flux->setReference(
'Reconversion automatique du ' . $now->format('d/m/Y')
. " pour le mode de reconversion " . $reconvmode
);
$this->operationUtils->executeOperations($flux);
}
$this->io->success('End');
$memoryUsage = memory_get_usage(true) / 1024 / 1024;
$this->io->text("Batch finished with memory: ${memoryUsage}M");
return 0;
}
}
<?php
namespace App\Controller;
use App\Utils\CustomEntityManager;
use App\Utils\TAVCotisationUtils;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Security;
class AdherentAdminController extends CRUDController
{
protected $em;
protected $security;
protected $tavCotisationUtils;
public function __construct(CustomEntityManager $em, Security $security, TAVCotisationUtils $tavCotisationUtils)
{
$this->em = $em;
$this->security = $security;
$this->tavCotisationUtils = $tavCotisationUtils;
}
/**
* Prélèvement d'un adhérent pour ramener son solde sous son plafond.
*
* @param Request $request
* @param Uuid $id Id du prestataire
* @IsGranted({"ROLE_SUPER_ADMIN", "ROLE_ADMIN_SIEGE", "ROLE_TRESORIER"})
* @return Response
*/
public function withdrawDownToTheCeilingAction(Request $request, $id): Response
{
$adherent = $this->admin->getSubject();
if (!$adherent) {
throw new NotFoundHttpException(sprintf('Impossible de trouver l\'adhérent avec l\'id: %s', $id));
}
$amountDiff = $this->tavCotisationUtils->withdrawDownToTheCeiling($adherent);
$this->em->flush();
$this->addFlash(
'sonata_flash_success',
'Prélèvement de ' . -$amountDiff . ' MonA' . ' effectué.'
);
return new RedirectResponse(
$this->admin->generateUrl('list', ['filter' => $this->admin->getFilterParameters()])
);
}
}
......@@ -2,11 +2,14 @@
namespace App\Controller;
use AndrewSvirin\Ebics\Builders\CustomerCreditTransfer\CustomerCreditTransferBuilder;
use App\Entity\Adherent;
use App\Entity\Don;
use App\Entity\Flux;
use App\Entity\GlobalParameter;
use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Entity\Reconversion;
use App\Entity\User;
use App\Enum\CurrencyEnum;
use App\Flux\FluxInterface;
......@@ -16,6 +19,7 @@ use App\Utils\TAVCotisationUtils;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Gamez\Symfony\Component\Serializer\Normalizer\UuidNormalizer;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sonata\Exporter\Handler;
use Sonata\Exporter\Source\DoctrineORMQuerySourceIterator;
use Sonata\Exporter\Writer\CsvWriter;
......@@ -30,6 +34,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
......@@ -74,6 +79,8 @@ class FluxController extends AbstractController
protected $operationUtils;
protected $tokenGenerator;
protected $validator;
protected $userManager;
protected $router;
public function __construct(
Security $security,
......@@ -86,7 +93,9 @@ class FluxController extends AbstractController
TAVCotisationUtils $tavCotisationsUtils,
TokenGeneratorInterface $tokenGenerator,
ValidatorInterface $validator,
CsrfTokenManagerInterface $tokenManager
CsrfTokenManagerInterface $tokenManager,
UserManagerInterface $userManager,
RouterInterface $router
) {
$this->security = $security;
$this->em = $em;
......@@ -99,6 +108,8 @@ class FluxController extends AbstractController
$this->validator = $validator;
$this->tokenManager = $tokenManager;
$this->tavCotisationsUtils = $tavCotisationsUtils;
$this->userManager = $userManager;
$this->router = $router;
}
protected function manageFluxForm(Request $request, Form $form, $template = '@kohinos/flux/transaction.html.twig', $params = [])
......@@ -168,6 +179,101 @@ class FluxController extends AbstractController
}
/**
* @param Request $request
* @Route("/credit-transfer-file", name="credit_transfer_file")
* @IsGranted({"ROLE_TRESORIER", "ROLE_SUPER_ADMIN"})
* @return Response
*/
public function creditTransferFileAction(Request $request)
{
//raison, bic and iban from debitor are fetched in global parameters
$globalParametersRepository = $this->em->getRepository(GlobalParameter::class);
$raison = $globalParametersRepository->val(GlobalParameter::VIREMENT_RECONVERSION_RAISON_GESTIONNAIRE);
$bic = $globalParametersRepository->val(GlobalParameter::VIREMENT_RECONVERSION_BIC_GESTIONNAIRE);
$iban = $globalParametersRepository->val(GlobalParameter::VIREMENT_RECONVERSION_IBAN_GESTIONNAIRE);
//make sure raison, bic and iban are not empty
if(!$raison || !$bic || !$iban) {
$this->addFlash(
'sonata_flash_error',
"Opération annulée car la raison, l'IBAN ou le BIC du gestionnaire pour les virements de reconversions est vide."
);
return $this->redirect($this->router->generate('index') . 'admin/app/reconversion/list');
}
//SEPA
$builder = new CustomerCreditTransferBuilder();
$customerCreditTransfer = $builder
->createInstance(
$bic,
$iban,
$raison
);
$reconversions = $this->em->getRepository(Reconversion::class)->findBy(["reconverti" => false]);
if(!$reconversions) {
$this->addFlash(
'sonata_flash_error',
"Aucune reconversion à traiter."
);
return $this->redirect($this->router->generate('index') . 'admin/app/reconversion/list');
}
foreach($reconversions as $r) {
/* @var Reconversion $r */
/* @var Prestataire $presta */
$presta = $r->getExpediteur();
if(!$presta || !$presta->isEnabled()) {
//fail ; do not flush : we don't want to toggle reconverti when file can't be generated
$this->addFlash(
'sonata_flash_error',
"Opération annulée car le prestataire " . $presta->getRaison() . " est désactivé ou introuvable."
);
return $this->redirect($this->router->generate('index') . 'admin/app/reconversion/list');
}
if(!$presta->getIban() || !$presta->getBic()) {
//fail ; do not flush : we don't want to toggle reconverti when file can't be generated
$this->addFlash(
'sonata_flash_error',
"Opération annulée car l'IBAN ou le BIC du prestataire " . $presta->getRaison() . " est vide."
);
return $this->redirect($this->router->generate('index') . 'admin/app/reconversion/list');
}
$customerCreditTransfer
->addTransaction(
$presta->getBic(),
$presta->getIban(),
$presta->getRaison(),
$r->getMontant(),
'EUR',
'Reconversion MonA vers EUR'
);
$r->setReconverti(true);
}
$filename = sprintf(
'credit_transfer_file_%s.%s',
date('Y_m_d_H_i_s', strtotime('now')),
'xml'
);
//projectDir is composer.json
$dir = $this->getParameter('kernel.project_dir') . "/reconversions";
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$path = $dir . "/" . $filename;
$customerCreditTransfer->popInstance()->save($path);
$this->em->flush();
return $this->file($path);
}
/**
* Export all operations for a user role.
*
* @param Request $request Request
......
......@@ -8,15 +8,22 @@ use App\Entity\AccountPrestataire;
use App\Entity\AccountSiege;
use App\Entity\Comptoir;
use App\Entity\Geoloc;
use App\Entity\GeolocPrestataire;
use App\Entity\GlobalParameter;
use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Usergroup;
use App\Enum\CurrencyEnum;
use App\Form\Type\DistributorSelfEvalPrestaQuizType;
use App\Form\Type\SelfEvalPrestaQuizType;
use App\Form\Type\InfosPrestaQuizType;
use App\Form\Type\InstallFormType;
use App\Form\Type\ProducerSelfEvalPrestaQuizType;
use App\Security\LoginAuthenticator;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\FOSUserEvents;
......@@ -36,10 +43,10 @@ use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Security as Secur;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use Symfony\Component\Security\Core\Security;
class IndexController extends AbstractController
{
......@@ -53,6 +60,7 @@ class IndexController extends AbstractController
private $tokenStorage;
private $router;
private $security;
private $mailer;
public function __construct(
EventDispatcherInterface $eventDispatcher,
......@@ -64,7 +72,8 @@ class IndexController extends AbstractController
LoginAuthenticator $authenticator,
TokenStorageInterface $tokenStorage,
RouterInterface $router,
Security $security
Secur $security,
\Swift_Mailer $mailer
) {
$this->eventDispatcher = $eventDispatcher;
$this->em = $em;
......@@ -76,7 +85,8 @@ class IndexController extends AbstractController
$this->tokenStorage = $tokenStorage;
$this->router = $router;
$this->security = $security;
}
$this->mailer = $mailer;
}
/**
* @Route("/", name="index")
......@@ -105,10 +115,10 @@ class IndexController extends AbstractController
}
if (
isset($_GET["upswd"])
&& $_GET["upswd"] == "success"
isset($_GET['upswd'])
&& 'success' == $_GET['upswd']
&& $this->security->isGranted('ROLE_ADHERENT')
&& $this->getParameter('tav_env') == true
&& true == $this->getParameter('tav_env')
) {
$this->addFlash(
'warning',
......@@ -116,6 +126,12 @@ class IndexController extends AbstractController
);
}
// Redirect prestaire at login if self evaluation process is enabled
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('index');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
return $this->render('@kohinos/' . $template, [
'news' => [],
'last_username' => $lastUsername,
......@@ -124,6 +140,88 @@ class IndexController extends AbstractController
}
/**
* This method is core method to guide the new
* prestataire when using presta_self_init_and_eval mode.
* It is basically a state machine that returns the appropriate
* route path depending on current state (details in the body).
* It also provides the prestataire object itself for convenience
* (not so easy to fetch from user when prestataire is disabled).
* @param $origin
* @return array|null[]
*/
private function getAndDispatchPrestataire($origin)
{
/*
* Go to home page if process is not active or if user is not connected as adherent.
* Second filter is just an easy way to early detect anonymous users.
* Why adherent ? Because once prestataire is created disabled and after is has been linked
* to some user, this user will connect to the platform as an adherent (because prestataire is disabled).
*/
if (!$this->getParameter('presta_self_init_and_eval')
|| !$this->security->isGranted('ROLE_ADHERENT')) { //not sure ROLE_ADHERENT is the proper check here
$destination = 'index';
return $destination !== $origin ? [$destination, null] : [null, null];
}
//Fetch the prestataire even when it is disabled
/* @var ArrayCollection $prestas */
if ($this->em->getFilters()->isEnabled('enabled_filter')) {
$this->em->getFilters()->disable('enabled_filter');
}
$prestas = $this->em->getRepository(Prestataire::class)->findByData(['user' => $this->security->getUser()]);
if (!$this->em->getFilters()->isEnabled('enabled_filter')) {
$this->em->getFilters()->enable('enabled_filter');
}
if (!$prestas) {
$destination = 'index';
return $destination !== $origin ? [$destination, null] : [null, null];
}
/* @var Prestataire $presta */
$presta = $prestas[0];
//Go to home page if the prestataire is enabled
if ($presta->isEnabled()) {
$destination = 'index';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
//Go to "all forms already sent, please just wait now" page if questionnaire has been submitted already
$prestaQuizz = $presta->getSelfEvalPrestaQuiz();
if ($prestaQuizz && $prestaQuizz->isSubmitted) {
$destination = 'prestaquiz-sent';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
//Go to questionnaire if first identification form has been submitted
//Condition is detected using market_channel_function attributes which is
//set in the first form (it is also the only mandatory attribute to start
//the self-eval part)
if ($presta->getMarketChannelFunction()) {
$destination = 'prestaquiz-selfeval';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
//In other cases, start the subscription process
$destination = 'prestaquiz-infos';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
/**
* @Route("/prestaquiz-sent", name="prestaquiz-sent")
*/
public function prestaQuizSent(Request $request): Response
{
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-sent');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
return $this->render('@kohinos/tav/prestaquiz/sent.html.twig');
}
/**
* @Route("/manifest.json", name="manifest")
*/
public function manifest(Request $request)
......@@ -140,6 +238,178 @@ class IndexController extends AbstractController
}
/**
* @Route("/prestaquiz-infos", name="prestaquiz-infos")
*/
public function prestaQuizInfosAction(Request $request): Response
{
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-infos');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
/* @var Prestataire $presta */
if(!$presta->getGeolocs() || $presta->getGeolocs()->isEmpty()) {
$address = new GeolocPrestataire();
$presta->addGeoloc($address);
}
$form = $this->createForm(InfosPrestaQuizType::class, $presta);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->security->getUser()->setFirstName($form->get('userFirstName')->getData());
$this->security->getUser()->setLastName($form->get('userLastName')->getData());
$this->em->flush();
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-infos');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
}
return $this->render('@kohinos/tav/prestaquiz/infos.html.twig', [
'form' => $form->createView(),
]);
}
/**
* @Route("/prestaquiz-selfeval", name="prestaquiz-selfeval")
*/
public function prestaQuizSelfEvalAction(Request $request): Response
{
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-selfeval');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
/* @var Prestataire $presta */
$formClass = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;
// Get existing form if it has been saved for future completion
$quiz = $presta->getSelfEvalPrestaQuiz();
if (null == $quiz) {
$quiz = new SelfEvalPrestaQuiz();
}
$form = $this->createForm($formClass, $quiz);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$definitiveSubmit = $form->get('submit_presta_quizz')->getData() == '1';
$presta->setSelfEvalPrestaQuiz($quiz);
$quiz->isSubmitted = $definitiveSubmit;
$this->em->persist($quiz);
$this->em->flush();
//Send email !
if ($definitiveSubmit) {
$this->notifyGestionnaireAfterPrestaquizSent($presta);
}
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-selfeval');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
}
$tmpl = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
'@kohinos/tav/prestaquiz/distributor.html.twig'
: '@kohinos/tav/prestaquiz/producer.html.twig';
return $this->render($tmpl, [
'form' => $form->createView(),
]);
}
/**
* @Route("/quiz-presta-edit", name="quiz-presta-edit")
* @Security("is_granted('ROLE_PRESTATAIRE')")
*/
/* Late presta edit (after definitive submission) :
* As we are not in the somehow complex subscription tunnel context anymore,
* I found easier to create a new controller
* instead of reusing prestaQuizSelfEvalAction
*/
public function quizPrestaEditAction(Request $request): Response
{
//Look for enabled prestataire with current user (fail if no enabled prestataire has been found)
$prestas = $this->em->getRepository(Prestataire::class)->findByData(['user' => $this->security->getUser()]);
if(!$prestas) {
throw new \Exception('Vous n\'êtes pas autorisé à accèder à cette page.');
} else {
$presta = $prestas[0];
}
/* @var Prestataire $presta */
$formClass = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;
// Block if no quiz (e.g. user access this controleur in non presta_self_init_and_eval env) or
// if presta has been enabled while questionnaire has not been submitted yet (should not occur)
$quiz = $presta->getSelfEvalPrestaQuiz();
if (null == $quiz || !$quiz->isSubmitted) {
throw new \Exception('Aucun questionnaire soumis trouvé.');
}
$form = $this->createForm($formClass, $quiz, ["mode" => SelfEvalPrestaQuizType::PRESTA_EDIT_AFTER_DEFINITIVE_SUBMISSION]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->em->flush();
// Notify group administrator that the user updated their self evaluation
$this->notifyGestionnaireAfterPrestaquizSent($presta, 'edit');
$this->addFlash(
'success',
'Modifications enregistrées !'
);
return $this->redirectToRoute('show_prestataire', ['slug' => $presta->getSlug()]);
}
$tmpl = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
'@kohinos/tav/prestaquiz/distributor.html.twig'
: '@kohinos/tav/prestaquiz/producer.html.twig';
return $this->render($tmpl, [
'form' => $form->createView(),
]);
}
/**
* Notify by email the group administrator when a prestataire submits or edits their
* self evaluation quizz.
*
* @param $type submit|edit, action performed by the user. Default: 'submit'.
*/
private function notifyGestionnaireAfterPrestaquizSent(Prestataire $presta, $type="submit")
{
$user = $this->security->getUser();
$gestionnaires = $presta->getGroupe()->getGestionnaires();
if(!$gestionnaires || $gestionnaires->isEmpty() || !$gestionnaires->first()->getEmail()) {
$to = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_CONTACT_EMAIL);
} else {
$to = $gestionnaires->first()->getEmail();
}
$subjectKeyWord = $type == "submit" ? "Réception" : "Édition";
$subject = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL)
. ' : ' . $subjectKeyWord . ' d\'un questionnaire de point de vente';
$contentKeyWord = $type == "submit" ? "soumettre" : "modifier";
$mail = (new \Swift_Message($subject))
->setFrom($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL)) //using $from here sometimes fails with 550 5.7.1 Sender mismatch
->setTo($to)
->setBody("L'utilisateur " . $user->getUsername()
. " vient de " . $contentKeyWord . " un questionnaire d'auto-évaluation pour le point de vente " . $presta->getRaison() . "."
);
$this->mailer->send($mail);
}
/**
* @Route("/installation", name="installation")
*/
public function installationAction(Request $request)
......@@ -379,7 +649,7 @@ class IndexController extends AbstractController
$this->session->set('_groupegere', $groupe);
$this->reloadUserTokenFromGroup($group, $request);
if ($this->getParameter('tav_env') == true) {
if (true == $this->getParameter('tav_env')) {
return $this->redirectToRoute('index');
} else {
return $this->redirectToRoute('sonata_admin_dashboard');
......@@ -412,7 +682,7 @@ class IndexController extends AbstractController
$this->session->set('_comptoirgere', $comptoir);
$this->reloadUserTokenFromGroup($group, $request);
if ($this->getParameter('tav_env') == true) {
if (true == $this->getParameter('tav_env')) {
return $this->redirectToRoute('index');
} else {
return $this->redirectToRoute('sonata_admin_dashboard');
......
......@@ -2,25 +2,26 @@
namespace App\Controller;
use App\Entity\GlobalParameter;
use App\Entity\Payment;
use App\Entity\User;
use App\Security\LoginAuthenticator;
use App\Utils\PaymentUtils;
use Doctrine\ORM\EntityManagerInterface;
use Payum\Core\Payum;
use Payum\Core\Request\GetHumanStatus;
use Payum\Core\Request\Notify;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Translation\TranslatorInterface;
use Payum\Core\Payum;
use Payum\Core\Request\Notify;
use Payum\Core\Request\GetHumanStatus;
use App\Entity\Flux;
use App\Entity\Payment;
use App\Entity\User;
use App\Entity\GlobalParameter;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use App\Security\LoginAuthenticator;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Translation\TranslatorInterface;
/**
* Gestion des paiements avec Payum
* Gestion des paiements avec Payum.
*/
class PaymentController extends AbstractController
{
......@@ -29,22 +30,25 @@ class PaymentController extends AbstractController
protected $payum;
protected $authenticator;
protected $guardHandler;
protected $paymentUtils;
public function __construct(EntityManagerInterface $em,
TranslatorInterface $translator,
LoginAuthenticator $authenticator,
GuardAuthenticatorHandler $guardHandler,
Payum $payum)
Payum $payum,
PaymentUtils $paymentUtils)
{
$this->em = $em;
$this->translator = $translator;
$this->payum = $payum;
$this->authenticator = $authenticator;
$this->guardHandler = $guardHandler;
$this->paymentUtils = $paymentUtils;
}
/**
* Crée une instance de Payment, les tokens associés, et redirige vers la page de paiement
* Crée une instance de Payment, les tokens associés, et redirige vers la page de paiement.
*/
public function preparePaymentAction(Form $form, $type, $extra_data = null)
{
......@@ -70,19 +74,20 @@ class PaymentController extends AbstractController
],
'expediteur' => ['id'],
'destinataire' => ['id'],
'operateur' => ['id']]
'operateur' => ['id'], ],
]);
$jsondata = $serializer->serialize($data, 'json');
// Prepare CB Payment
if ($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_PAYZEN) === 'true') {
if ('true' === $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_PAYZEN)) {
$gatewayName = 'payzen';
} else {
$this->addFlash(
'error',
$this->translator->trans('Une erreur est survenue due à la configuration du paiement dans l\'application. Il est pour l\'instant impossible de payer par CB, merci de contacter votre monnaie locale.')
);
return $this->redirectToRoute('index');
}
......@@ -99,8 +104,8 @@ class PaymentController extends AbstractController
$payment->setExtraData($extra_data);
}
if ($type == Payment::TYPE_ADHESION) {
$payment->setTotalAmount($form->get('cotisation')->get('montant')->getData()*100); // 1.23 EUR
if (Payment::TYPE_ADHESION == $type) {
$payment->setTotalAmount($form->get('cotisation')->get('montant')->getData() * 100); // 1.23 EUR
$payment->setClientId('Nouvel adhérent');
$payment->setClientEmail($form->get('user')->get('email')->getData());
} else {
......@@ -114,6 +119,13 @@ class PaymentController extends AbstractController
$payment->setClientEmail($this->getUser()->getEmail());
}
if (Payment::TYPE_PAIEMENT_RECURRENT_COTISATION_TAV == $type) {
$payment->setRecurrenceAmount($form->get('montant')->getData() * 100);
$payment->setIsRecurrent(true);
$payment->setRecurrenceMonthsCount($form->get('nombreMois')->getData());
$payment->setRecurrenceMonthDay($form->get('jourPrelevement')->getData());
}
$storage->update($payment);
$captureToken = $this->payum->getTokenFactory()->createCaptureToken(
......@@ -135,8 +147,116 @@ class PaymentController extends AbstractController
return $this->redirect($captureToken->getTargetUrl());
}
/* COMMENT LES FLUX SONT-ILS CREES SUITE A LA CREATION D'UN PAIEMENT PAYZEN ?
*
*
*
* CAS 1 : Paiement standard
* Deux voies de signalement coexistent :
*
* Voie 1 : via la notification instantannée payzen.
* Un paramètre appelé "URL de notification à la fin du paiement" est configuré dans le backoffice payzen et
* est renseigné à payement/notify. Cette valeur est utilisée par payzen, sauf si la variable vads_url_check
* est transmise, auquel cas c'est cette dernière valeur qui prime.
* Ici, vads_url_check est transmise à la valeur payment/notify/{token}.
* Remarque : son usage systématique n'est pas recommandé par Payzen.
* L'URL est ainsi captée par NotifyController.php.
*
* Voie 2 : via la redirection du client.
* Cette voie n'est pas garantie (si l'utilisateur ferme son navigateur assez tôt, il ne sera pas redirigé).
* A vérifier : l'URL de redirection est la combinaison entre une configuration côté backoffice et
* un complément que nous transmettons.
* Lorsque la redirection a bien lieu, c'est donc le contrôleur doneAction de PaymentController.php qui est sollicité,
* avec une requête au format payement/done/?payum_token={token}
*
* Remarquons dans ces deux stratégie la présence d'un token permettant l'authentification de l'agent Payzen et
* la récupération de l'objet paiement préalablement enregistré en base.
*
*
*
* CAS 2 : Paiement récurrent
*
* Dans le cadre de notre implémentation, un paiement récurrent commence par un paiement initial (occurence n°0).
* Pour cette occurence n°0, au niveau du mode de communication, on est sur un processus en apparence similaire au
* CAS 1. Un examen minutieux de ce process m'a cependant permis de mettre en évidence des comportements que
* je n'ai pas réussi à comprendre :
* En ouvrant les vannes pour tester le déclenchement d'une création de flux à chaque notification rattachée
* à un paiement récurrent, j'ai observé la génération de pas moins de 5 événements, opérant donc
* un accroissement de solde de +750 mona au lieu de +150 mona !!!
* - 2 événements via /payement/notify/{token}
* - 3 événements après l'execution de la ligne $gateway->execute($status = new GetHumanStatus($token)); dans doneAction.
*
* Le fonctionnement du module Payum est sans doute très intelligent mais il est trop complexe à maitriser pour moi.
* Il fait également appel à des mécanismes dont l'usage systématique est déconseillé par Payzen (utilisation de la variable
* vads_url_check).
*
* Bref. De toute façon, la gestion des notifications successives d'un paiement récurrent ne peut pas être gérée
* via les voies 1 ou 2 en l'état.
* En effet la variable vads_url_check ne fonctionne pas en mode récurrent et Payzen nous notifie à l'aide
* d'une requête post au format de l'"URL de notification à la création d'un abonnement" (mal nommé), configurée à payement/notify,
* (mais on aurait pu mettre autre chose pour plus de clarté). Il faut donc créer un controlleur spécifique qui va traiter
* cette notification sans disposer du token utilisé par les voies 1 ou 2.
*
* Voie 3 : le traitement de la requête se fait finalement simplement dans notifyRecurringPaymentAction sans utiliser
* les mécanismes complexes de Payum.
*
* Yvon KERDONCUFF
* 26/03/2024
*/
/**
* Collects payzen recurring payment payment occurence in place of default payum system.
*
* Payum default controler is not able to catch URL recurring payment notifications
* because payzen actual used URL has shape /payment/notify instead of /payment/notify/token.
*
* @param Request $request
*
* @return Response
* @Route("/payment/notify", name="notify_recurring_payment")
*/
public function notifyRecurringPaymentAction(Request $request)
{
$vads_cust_email = $request->request->get('vads_cust_email');
//Look for recurring payments from this client.
$recurringPayments = $this->em->getRepository(Payment::class)->findBy([
'isRecurrent' => true,
'clientEmail' => $vads_cust_email,
]);
if (!$recurringPayments) {
//return error
return new Response('No recurring payments with email ' . $vads_cust_email, 405);
}
$vads_identifier = $request->request->get('vads_identifier');
$vads_trans_status = $request->request->get('vads_trans_status');
$new_status = strtolower($vads_trans_status);
foreach ($recurringPayments as $payment) {
//Just look for one valid payment.
if (
$payment->getDetails()
&& array_key_exists('vads_identifier', $payment->getDetails())
&& $payment->getDetails()['vads_identifier'] == $vads_identifier
) {
if (
GetHumanStatus::STATUS_CAPTURED == $new_status
|| GetHumanStatus::STATUS_AUTHORIZED == $new_status
) {
$this->paymentUtils->handlePayzenNotificationCore($payment);
$this->em->flush();
}
return new Response('Recurring payment occurence taken into account.', 200);
}
}
//return error
return new Response('No recurring payments with vads_identifier ' . $vads_identifier, 405);
}
/**
* Fonction de retour sur le site par l'utilisateur après paiement
* Ce contrôleur est sollicité lorsque Payzen renvoie le cotisant sur l'URL de retour,
*
* @Route("/payment/done/", name="payment_done")
*/
......@@ -154,16 +274,14 @@ class PaymentController extends AbstractController
$gateway->execute($status = new GetHumanStatus($token));
$payment = $status->getFirstModel();
if ($payment->getStatus() == GetHumanStatus::STATUS_NEW) {
// No notification arrived at this point: execute Notify action
if (GetHumanStatus::STATUS_NEW == $payment->getStatus()) {
$gateway->execute(new Notify($token));
} else {
// Invalidate token
$this->payum->getHttpRequestVerifier()->invalidate($token);
}
// Set flash message according to payment status
if ($payment->getStatus() == GetHumanStatus::STATUS_CAPTURED || $payment->getStatus() == GetHumanStatus::STATUS_AUTHORIZED) {
if (GetHumanStatus::STATUS_CAPTURED == $payment->getStatus() || GetHumanStatus::STATUS_AUTHORIZED == $payment->getStatus()) {
$type = $payment->getDescription();
if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type || Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) {
......@@ -171,12 +289,12 @@ class PaymentController extends AbstractController
'success',
$this->translator->trans('Achat de monnaie locale bien effectué !')
);
} else if (Payment::TYPE_COTISATION_ADHERENT == $type || Payment::TYPE_COTISATION_PRESTA == $type) {
} elseif (Payment::TYPE_COTISATION_ADHERENT == $type || Payment::TYPE_COTISATION_PRESTA == $type) {
$this->addFlash(
'success',
$this->translator->trans('Cotisation bien reçue. Merci !')
);
} else if (Payment::TYPE_ADHESION == $type) {
} elseif (Payment::TYPE_ADHESION == $type) {
$this->addFlash(
'success',
$this->translator->trans('Votre adhésion a bien été prise en compte, bienvenue !')
......@@ -185,21 +303,20 @@ class PaymentController extends AbstractController
// Connect new user
return $this->guardHandler
->authenticateUserAndHandleSuccess(
$this->em->getRepository(User::class)->findOneBy(array('id' => $payment->getClientId())),
$this->em->getRepository(User::class)->findOneBy(['id' => $payment->getClientId()]),
$request,
$this->authenticator,
'main'
);
} else if (Payment::TYPE_PAIEMENT_COTISATION_TAV == $type) {
} elseif (Payment::TYPE_PAIEMENT_COTISATION_TAV == $type || Payment::TYPE_PAIEMENT_RECURRENT_COTISATION_TAV) {
$this->addFlash(
'success',
$this->translator->trans('Cotisation payée !')
);
}
} else if ($payment->getStatus() == GetHumanStatus::STATUS_CANCELED ||
$payment->getStatus() == GetHumanStatus::STATUS_EXPIRED ||
$payment->getStatus() == GetHumanStatus::STATUS_FAILED)
{
} elseif (GetHumanStatus::STATUS_CANCELED == $payment->getStatus() ||
GetHumanStatus::STATUS_EXPIRED == $payment->getStatus() ||
GetHumanStatus::STATUS_FAILED == $payment->getStatus()) {
$this->addFlash(
'error',
$this->translator->trans('La transaction a été annulée.')
......@@ -208,5 +325,4 @@ class PaymentController extends AbstractController
return $this->redirectToRoute('index');
}
}
......@@ -6,10 +6,14 @@ use App\Entity\CotisationAdherent;
use App\Entity\CotisationPrestataire;
use App\Entity\Prestataire;
use App\Enum\MoyenEnum;
use App\Form\Type\DistributorSelfEvalPrestaQuizType;
use App\Form\Type\ProducerSelfEvalPrestaQuizType;
use App\Form\Type\SelfEvalPrestaQuizType;
use App\Utils\CustomEntityManager;
use DateTime;
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Security;
......@@ -115,4 +119,44 @@ class PrestataireAdminController extends CRUDController
$this->admin->generateUrl('list', ['filter' => $this->admin->getFilterParameters()])
);
}
/**
* Visualisation et complétion de la partie réservée à l'admin sur un questionnaire prestataire
*
* @param Request $request
* @param Uuid $id Id du prestataire
*
* @return Response
*/
public function reviewprestaquizAction(Request $request, $id): Response
{
$prestataire = $this->admin->getSubject();
if (!$prestataire) {
throw new NotFoundHttpException(sprintf('Impossible de trouver le prestataire avec l\'id: %s', $id));
}
$quiz = $prestataire->getSelfEvalPrestaQuiz();
$formClass = Prestataire::DISTRIBUTOR === $prestataire->getMarketChannelFunction() ?
DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;
$form = $this->createForm($formClass, $quiz, ["mode" => SelfEvalPrestaQuizType::ADMIN_EDIT]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->em->flush();
return new RedirectResponse(
$this->admin->generateUrl('list', ['filter' => $this->admin->getFilterParameters()])
);
}
$tmpl = Prestataire::DISTRIBUTOR === $prestataire->getMarketChannelFunction() ?
'@kohinos/tav/prestaquiz/distributor.html.twig'
: '@kohinos/tav/prestaquiz/producer.html.twig';
return $this->render($tmpl, [
'form' => $form->createView(),
'prestataire' => $prestataire
]);
}
}
......@@ -6,6 +6,10 @@ use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Entity\Rubrique;
use App\Entity\Flux;
use App\Entity\SelfEvalPrestaQuiz;
use App\Form\Type\DistributorSelfEvalPrestaQuizType;
use App\Form\Type\ProducerSelfEvalPrestaQuizType;
use App\Form\Type\SelfEvalPrestaQuizType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Annotation\Route;
......@@ -39,9 +43,22 @@ class PrestatairesController extends FrontController
return new RedirectResponse($this->router->generate('index'));
}
return $this->render('@kohinos/presta/show.html.twig', [
'presta' => $prestataire,
]);
$templateData = ['presta' => $prestataire];
// If feature activated, display self evaluation form
if ($this->getParameter('presta_self_init_and_eval') == true) {
$quiz = $prestataire->getSelfEvalPrestaQuiz();
if ($quiz->isSubmitted) {
$formClass = Prestataire::DISTRIBUTOR === $prestataire->getMarketChannelFunction() ?
DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;
$form = $this->createForm($formClass, $quiz, ["mode" => SelfEvalPrestaQuizType::READONLY]);
$templateData['form'] = $form->createView();
}
}
return $this->render('@kohinos/presta/show.html.twig', $templateData);
}
/**
......
......@@ -11,6 +11,7 @@ use App\Entity\TransactionAdherentAdherent;
use App\Entity\TransactionAdherentPrestataire;
use App\Form\Type\AchatMonnaieAConfirmerAdherentFormType;
use App\Form\Type\AchatMonnaieAdherentFormType;
use App\Form\Type\AchatMonnaieAdherentRecurrentFormType;
use App\Form\Type\AdherentInfosFormType;
use App\Form\Type\TransactionAdherentAdherentFormType;
use App\Form\Type\TransactionAdherentPrestataireFormType;
......@@ -122,6 +123,42 @@ class UserAdherentController extends FluxController
}
/**
* Validations before proceeding to payment.
*
* Returns error message or null if no error.
*/
private function paiementCotisTavValidation($flux) {
//Look for existing recurring payment
if($reason = $this->tavCotisationsUtils->checkExistingRecurringPayment($flux)) {
return $reason;
}
// Look for existing cotisation
if ($this->tavCotisationsUtils->checkExistingCotisation($flux)) {
return "Cotisation déjà payée ce mois-ci.";
}
$destinataire = $flux->getDestinataire();
// Look for cotisation data depending on active process
if (true == $this->getParameter('household_based_allowance')) {
$cotisationAmount = $destinataire->getCotisationAmount();
if (is_null($cotisationAmount) || is_null($destinataire->getHouseholdAdultCount())) {
return "Opération impossible : votre profil est incomplet, informations de cotisation manquantes. Veuillez contacter un administrateur.";
}
} else {
$profile = $destinataire->getProfilDeCotisation();
if (is_null($profile)) {
return "Opération impossible : vous n'avez pas de profil de cotisation associé. Veuillez contacter un administrateur.";
}
}
return null;
}
/**
* @Route("/adherent/paiement-cotis-tav/", name="paiementCotisTav")
* @IsGranted("ROLE_ADHERENT")
*/
......@@ -136,27 +173,13 @@ class UserAdherentController extends FluxController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// TODO: set CB payment when the functionality is validated
$flux = $form->getData();
// Look for existing cotisation
if ($this->tavCotisationsUtils->checkExistingCotisation($flux)) {
$validationError = $this->paiementCotisTavValidation($flux);
if (!is_null($validationError)) {
$this->addFlash(
'error',
$this->translator->trans('Cotisation déjà payée ce mois-ci.')
);
return $this->redirectToRoute('index');
}
$destinataire = $flux->getDestinataire();
$profile = $destinataire->getProfilDeCotisation();
if (is_null($profile)) {
$this->addFlash(
'error',
$this->translator->trans('Opération impossible : vous n\'avez pas de profil de cotisation associé. Veuillez contacter un administrateur.')
$this->translator->trans($validationError)
);
return $this->redirectToRoute('index');
......@@ -166,12 +189,13 @@ class UserAdherentController extends FluxController
$flux->setDon(null);
}
// TODO redirect to paiement
// Redirect to payment
return $this->forward('App\Controller\PaymentController::preparePaymentAction', [
'form' => $form,
'type' => Payment::TYPE_PAIEMENT_COTISATION_TAV // TODO
'type' => Payment::TYPE_PAIEMENT_COTISATION_TAV
]);
/* For test purposes, comment redirection and uncomment following part to skip payment */
// $this->em->persist($flux);
// $this->operationUtils->executeOperations($flux);
......@@ -194,6 +218,57 @@ class UserAdherentController extends FluxController
}
/**
* @Route("/adherent/paiement-reccurent-cotis-tav/", name="paiementRecurrentCotisTav")
* @IsGranted("ROLE_ADHERENT")
*/
public function paiementRecurrentCotisTavAction(Request $request)
{
if (empty($this->getUser()) || empty($this->getUser()->getAdherent())) {
return $this->redirectToRoute('index');
}
$entity = new AchatMonnaieAdherent();
$form = $this->createForm(AchatMonnaieAdherentRecurrentFormType::class, $entity);
$form->handleRequest($request);
if (!$form->isValid()) {
foreach($form->getErrors(true) as $error) {
$this->addFlash('error', $error->getMessage());
}
return $this->redirectToRoute('index');
}
if ($form->isSubmitted() && $form->isValid()) {
$flux = $form->getData();
$validationError = $this->paiementCotisTavValidation($flux);
if (!is_null($validationError)) {
$this->addFlash(
'error',
$this->translator->trans($validationError)
);
return $this->redirectToRoute('index');
}
if (null == $flux->getDon() || 0 == $flux->getDon()->getMontant()) {
$flux->setDon(null);
}
// Redirect to payment
return $this->forward('App\Controller\PaymentController::preparePaymentAction', [
'form' => $form,
'type' => Payment::TYPE_PAIEMENT_RECURRENT_COTISATION_TAV
]);
}
return $this->render('@kohinos/flux/transaction.html.twig', [
'form' => $form->createView(),
'title' => $this->translator->trans('Payer sa cotisation'),
]);
}
/**
* @Route("/adherent/demande/achat-monnaie/", name="achatMonnaieAConfirmerAdherent")
* @IsGranted("ROLE_ADHERENT")
*/
......
......@@ -215,6 +215,34 @@ class UserComptoirController extends FluxController
}
$destinataire = $flux->getDestinataire();
if ($this->getParameter('household_based_allowance') == true) {
/* Process: allowance based on household */
$cotisationAmount = $destinataire->getCotisationAmount();
// Verifications
if (is_null($cotisationAmount) || is_null($destinataire->getHouseholdAdultCount())) {
$this->addFlash(
'error',
$this->translator->trans("Opération impossible : le profil de l'habitant.e est incomplet, veuillez le compléter dans l'interface d'administration.")
);
return $this->redirectToRoute('index');
}
if (is_null($destinataire->getAllocationAmount())) {
$this->tavCotisationUtils->calculateAllowanceAccordingToHousehold($destinataire);
$this->em->persist($destinataire);
}
$flux->setMontant($cotisationAmount);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
// Apply cotisation rate, create new flux
$this->tavCotisationsUtils->applyHouseholdAllowance($flux);
} else {
/* Process: allowance based on cotisation profile with cotisation rate */
$profile = $destinataire->getProfilDeCotisation();
if (is_null($profile)) {
......@@ -234,6 +262,7 @@ class UserComptoirController extends FluxController
// Apply cotisation rate, create new flux
$this->tavCotisationsUtils->applyTauxCotisation($flux);
}
$this->em->flush();
......
......@@ -8,6 +8,7 @@ use App\Entity\CotisationPrestataire;
use App\Entity\GlobalParameter;
use App\Entity\Payment;
use App\Entity\Prestataire;
use App\Entity\TransactionPrestataireAdherent;
use App\Entity\User;
use App\Entity\TransactionAdherentPrestataire;
use App\Enum\MoyenEnum;
......@@ -242,15 +243,106 @@ class UserController extends AbstractController
return $this->redirectToRoute('index');
}
/**
* @param Request $request
* @param TransactionAdherentPrestataire $transactionAdherentPrestataire
* @return void
* @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"})
* @Route("/cancel-transaction-adherent-prestataire/{id}", name="cancel_transaction_adherent_prestataire")
*/
public function cancelTransactionAdherentPrestataireAction(Request $request, TransactionAdherentPrestataire $transactionAdherentPrestataire)
{
$adherent = $transactionAdherentPrestataire->getExpediteur();
$presta = $transactionAdherentPrestataire->getDestinataire();
//Make sure current user is destinataire of transaction
if(!in_array($this->getUser(),$presta->getUsers()->toArray())) {
$this->addFlash(
'error',
"Vous n'êtes pas autorisé à annuler cette transaction."
);
return $this->redirectToRoute('index');
}
//Make sure flux has not been cancelled already
if($transactionAdherentPrestataire->getCancellerFlux()) {
$this->addFlash(
'error',
"Cette transaction a déjà été annulée."
);
return $this->redirectToRoute('index');
}
// Check prestataire has enough funds
$balance = $presta->getEmlcAccount()->getBalance();
//I think this is unlikely in real situation but we need to make sure prestataire has enough founds
$montant = $transactionAdherentPrestataire->getMontant();
if ($balance < $montant) {
$this->addFlash(
'error',
'Fonds insuffisants pour annuler cette transaction.'
);
return $this->redirectToRoute('index');
}
//Create new transaction in opposite direction
$flux = new TransactionPrestataireAdherent();
$flux->setExpediteur($presta);
$flux->setDestinataire($adherent);
$flux->setOperateur($this->security->getUser());
$flux->setMontant($montant);
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$now = (new \Datetime('now'))->format('d/m/Y H:i:s');
$flux->setReference(
'Remboursement en Monnaie Solidaire du ' . $now . ' annule ' . $transactionAdherentPrestataire->getReference()
);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
//Mark original transaction as cancelled
$transactionAdherentPrestataire->setCancellerFlux($flux);
$this->em->flush();
$this->addFlash(
'success',
'La transaction a bien été annulée.'
);
return $this->redirectToRoute('index');
}
/**
* Payment terminal page.
*
* @Route("/encaissement", name="encaissement")
* @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"})
*/
public function encaissementAction(Request $request)
{
// If conventionnement process is activated, prevent access to payment terminal if prestataire conventionnement is not set
$conventionnementMode = $this->getParameter('presta_self_init_and_eval');
if ($conventionnementMode == true) {
$presta = $this->session->get('_prestagere');
$presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId());
$conventionnement = $presta->getConventionnement();
if ($conventionnement == null || $conventionnement == 0) {
$this->addFlash(
'error',
$this->translator->trans("Impossible d'accéder au terminal de paiement tant que votre conventionnement n'a pas été établi par un·e gestionnaire.")
);
return $this->redirectToRoute('index');
}
}
$form = $this->createForm(EncaissementFormType::class, null, []);
$form->handleRequest($request);
$validation = false;
$validation = false; // validation = user enters its code
$insufficientBalance = null; // if not null, will indicate for the template an insufficient founds situation
if ($form->isSubmitted()) {
$data = $form->getData();
......@@ -258,7 +350,10 @@ class UserController extends AbstractController
if ($form->isValid()) {
$adherent = $data["adherent"];
$adherent_code = $adherent->getPaymentCode();
$input_code = $data["payment_code"];
// Perform first step checks
if (empty($input_code)) {
// if adherent account isn't enabled
if (!$adherent->getUser()->isEnabled()) {
$this->addFlash(
......@@ -279,12 +374,30 @@ class UserController extends AbstractController
goto end;
}
$input_code = $data["payment_code"];
$validation = true; // When the form is submitted & valid, and the user account checks passed, we enter the validation process
// If entered amount is above maximum amount, depending on prestataire conventionnement
if ($conventionnementMode == true) {
$transaction_amount = floatval($data["montant"]);
$montantPanier = floatval($form['montantPanier']->getData());
$maxAmount = $montantPanier * floatval($conventionnement);
if ($transaction_amount > $maxAmount) {
$this->addFlash(
'error',
$this->translator->trans('Montant maximal en MonA dépassé. Veuillez modifier le montant en MonA.')
);
goto end;
}
}
// When the form is submitted & valid, and the user account checks passed, we enter the validation process
$validation = true;
if (empty($input_code)) {
// First step validated (set user & amount) -> go to validation
goto end;
} else {
// We're in the validation process
$validation = true;
}
// Check validation code
......@@ -324,15 +437,19 @@ class UserController extends AbstractController
$this->translator->trans('Solde de l\'habitant·e insuffisant')
);
$insufficientBalance = $balance;
goto end;
}
// Save transaction
// No error at this point: save transaction
$flux = new TransactionAdherentPrestataire();
$flux->setExpediteur($adherent);
if (!isset($presta)) {
$presta = $this->session->get('_prestagere');
$presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId());
}
$flux->setDestinataire($presta);
$flux->setOperateur($this->security->getUser());
......@@ -346,7 +463,15 @@ class UserController extends AbstractController
$this->operationUtils->executeOperations($flux);
$this->em->flush();
return $this->redirectToRoute('encaissementSuccess');
$redirectParams = [];
if ($form->has('montantPanier')) {
$transaction_amount = floatval($data["montant"]);
$montantPanier = floatval($form['montantPanier']->getData());
$redirectParams['remainingAmount'] = $montantPanier - $transaction_amount;
}
return $this->redirectToRoute('encaissementSuccess', $redirectParams);
} else {
$this->addFlash(
'error',
......@@ -356,10 +481,17 @@ class UserController extends AbstractController
}
end:
return $this->render('@kohinos/tav/encaissement_page.html.twig', [
$templateData = [
'form' => $form->createView(),
'validation' => $validation
]);
'validation' => $validation,
'insufficientBalance' => $insufficientBalance
];
if ($conventionnementMode == true) {
$templateData['conventionnement'] = $conventionnement;
}
return $this->render('@kohinos/tav/encaissement_page.html.twig', $templateData);
}
/**
......@@ -368,6 +500,13 @@ class UserController extends AbstractController
*/
public function encaissementSuccessAction(Request $request)
{
return $this->render('@kohinos/tav/payment_done_page.html.twig', []);
$templateData = [];
$remainingAmount = $request->query->get('remainingAmount');
if ($remainingAmount != '') {
$templateData['remainingAmount'] = $remainingAmount;
}
return $this->render('@kohinos/tav/payment_done_page.html.twig', $templateData);
}
}
......@@ -49,6 +49,8 @@ class UserPrestataireController extends FluxController
}
$this->em->persist($adresse);
}
// caissiers validations
if (!empty($form->get('newcaissiers')->getData())) {
$caissiers = explode(';', $form->get('newcaissiers')->getData());
$errors = [];
......@@ -102,6 +104,17 @@ class UserPrestataireController extends FluxController
} elseif (!$request->isXmlHttpRequest()) {
return new Response('', Response::HTTP_BAD_REQUEST);
}
} else {
foreach ($form->getErrors(true, true) as $error) {
// Add flash error message in case of error with the embedded form
if (str_contains($error->getCause()->getPropertyPath(), 'productFamily')) {
$this->addFlash(
'error',
$error->getMessage()
);
break;
}
}
}
return $this->redirectToRoute('index');
......
......@@ -10,6 +10,7 @@ use App\Entity\EntityTrait\HasEcompteEntity;
use App\Flux\AccountableInterface;
use App\Flux\AccountableObject;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\Doctrine\UuidGenerator;
......@@ -113,9 +114,41 @@ class Adherent extends AccountableObject implements AccountableInterface
*/
private $jourMailRappelCotisation;
/**
* @ORM\OneToMany(targetEntity=DependentChild::class, mappedBy="adherent", orphanRemoval=true, cascade={"persist"})
*/
private $dependentChildren;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $householdComposition;
/**
* @ORM\Column(type="integer", length=255, nullable=true)
*/
private $householdAdultCount;
/**
* On household based allowance process, define a cotisation amount for each adherent.
*
* @ORM\Column(type="float", nullable=true)
*/
private $cotisationAmount;
/**
* On household based allowance process, the allowance amountis calculated based on household data.
* Calculate and save the allocation amount when the household data is updated.
*
* @ORM\Column(type="float", nullable=true)
*/
private $allocationAmount;
public function __construct()
{
$this->accounts = new ArrayCollection();
$this->dependentChildren = new ArrayCollection();
}
public function getId()
......@@ -300,6 +333,18 @@ class Adherent extends AccountableObject implements AccountableInterface
return $this;
}
public function getHouseholdComposition(): ?string
{
return $this->householdComposition;
}
public function setHouseholdComposition(?string $householdComposition): self
{
$this->householdComposition = $householdComposition;
return $this;
}
public function getJourPrelevement(): ?int
{
return $this->jourPrelevement;
......@@ -312,6 +357,18 @@ class Adherent extends AccountableObject implements AccountableInterface
return $this;
}
public function getHouseholdAdultCount(): ?int
{
return $this->householdAdultCount;
}
public function setHouseholdAdultCount(?int $householdAdultCount): self
{
$this->householdAdultCount = $householdAdultCount;
return $this;
}
public function getMailRappelCotisation(): ?bool
{
return $this->mailRappelCotisation;
......@@ -335,4 +392,64 @@ class Adherent extends AccountableObject implements AccountableInterface
return $this;
}
/**
* @return Collection<int, DependentChild>
*/
public function getDependentChildren(): Collection
{
return $this->dependentChildren;
}
public function addDependentChild(DependentChild $dependentChild): self
{
if (!$this->dependentChildren->contains($dependentChild)) {
$this->dependentChildren[] = $dependentChild;
$dependentChild->setAdherent($this);
}
return $this;
}
public function removeDependentChild(DependentChild $dependentChild): self
{
if ($this->dependentChildren->removeElement($dependentChild)) {
// set the owning side to null (unless already changed)
if ($dependentChild->getAdherent() === $this) {
$dependentChild->setAdherent(null);
}
}
return $this;
}
public function getCotisationAmount(): ?float
{
return $this->cotisationAmount;
}
public function setCotisationAmount(?float $cotisationAmount): self
{
$this->cotisationAmount = $cotisationAmount;
return $this;
}
public function getAllocationAmount(): ?float
{
return $this->allocationAmount;
}
public function setAllocationAmount(?float $allocationAmount): self
{
$this->allocationAmount = $allocationAmount;
return $this;
}
public function getCeiling()
{
//This formula has been configured for ssa gironde project
return 2 * $this->allocationAmount;
}
}
......@@ -7,26 +7,32 @@ use App\Utils\OperationFactory;
use Doctrine\ORM\Mapping as ORM;
/**
* Application du taux de cotisation lors du paiement d'une cotisation (au sens TAV).
* Application du calcul de l'allocation (emlc reçues) lors du paiement d'une cotisation (au sens TAV).
*
* Au paiement d'une cotisation:
* - Un premier Flux est enregistré, correspondant à l'achat/vente d'emlc.
* - Puis on applique le taux défini dans le ProfilDeCotisation de l'adhérent,
* on crée un nouveau flux pour compléter la cositsation.
* - On crée un nouveau flux pour compléter la cotisation, en fonction du système de calcul choisi.
*
* Systèmes de calculs possibles :
* - On applique le taux défini dans le ProfilDeCotisation de l'adhérent
* - On calcule le montant à recevoir en fonction du foyer de l'adhérent
*
* @ORM\Entity
*/
class TauxCotisationApplication extends Flux
class CotisationTavApplication extends Flux
{
const TYPE_REVERSEMENT_COTISATION_ADHERENT = 'reversement_cotisation_adherent';
const TYPE_PRELEVEMENT_COTISATION_ADHERENT = 'prelevement_cotisation_adherent';
const TYPE_PRELEVEMENT_COTISATION_ADHERENT_DEPASSEMENT_PLAFOND = 'prelevement_cotisation_adherent_depassement_plafond';
const TYPE_PRELEVEMENT_COTISATION_ADHERENT_CORRECTION_SOLDE = 'prelevement_cotisation_adherent_correction_solde';
const TYPE_REVERSEMENT_COTISATION_ADHERENT_CORRECTION_SOLDE = 'reversement_cotisation_adherent_correction_solde';
/**
* @return string
*/
public function getParenttype(): string
{
return parent::TYPE_APPLICATION_TAUX_COTISATION;
return parent::TYPE_APPLICATION_COTISATION_TAV;
}
public function getAllOperations($em)
......
......@@ -7,12 +7,19 @@ use App\Utils\OperationFactory;
use Doctrine\ORM\Mapping as ORM;
/**
* En cas de taux < 1, l'adhérent•e reçoit moins d'emlc que ce qu'elle•il paye en € :
* Dans les cas suivants :
* - [Profil de Cotisation] La taux est inférieur à 1
* - [Allocation selon foyer] Le montant reçu calculé est inférieur au montant payé
* - [Allocation selon foyer] Un administrateur effectue une opération manuelle de prélèvement de l'adhérent
* pour ramener son solde au niveau de son plafond.
* - [Allocation selon foyer] Un administrateur effectue une opération de correction de solde qui conduit à un prélèvement.
*
* L'adhérent•e reçoit moins d'emlc que ce qu'elle•il paye en €,
* un second flux est créé pour prélever le complément de la cotisation.
*
* @ORM\Entity
*/
class TauxCotisationPrelevement extends TauxCotisationApplication
class CotisationTavPrelevement extends CotisationTavApplication
{
/**
* @ORM\OneToOne(targetEntity="Adherent")
......
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Child class used to notify adherents by email.
*
* - [Allocation selon foyer] Un administrateur effectue une opération de correction de solde qui conduit à un prélèvement.
* @ORM\Entity
*/
class CotisationTavPrelevementCorrectionSolde extends CotisationTavPrelevement
{
/**
* @return string
*/
public function getType(): string
{
return parent::TYPE_PRELEVEMENT_COTISATION_ADHERENT_CORRECTION_SOLDE;
}
public function getUsersToNotify()
{
return [
'expediteurs' => [$this->getExpediteur()->getUser()],
];
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Child class used to notify adherents by email.
*
* - [Allocation selon foyer] Un administrateur effectue une opération manuelle de prélèvement de l'adhérent
* pour ramener son solde au niveau de son plafond.
* @ORM\Entity
*/
class CotisationTavPrelevementDepassementPlafond extends CotisationTavPrelevement
{
/**
* @return string
*/
public function getType(): string
{
return parent::TYPE_PRELEVEMENT_COTISATION_ADHERENT_DEPASSEMENT_PLAFOND;
}
public function getUsersToNotify()
{
return [
'expediteurs' => [$this->getExpediteur()->getUser()],
];
}
}
......@@ -7,12 +7,17 @@ use App\Utils\OperationFactory;
use Doctrine\ORM\Mapping as ORM;
/**
* En cas de taux > 1, l'adhérent•e reçoit plus d'emlc que ce qu'elle•il paye en € :
* * Dans les cas suivants :
* - [Profil de Cotisation] La taux est supérieur à 1
* - [Allocation selon foyer] Le montant reçu calculé est supérieur au montant payé
* - [Allocation selon foyer] Un administrateur effectue une opération de correction de solde qui conduit à un reversement.
*
* L'adhérent•e reçoit plus d'emlc que ce qu'elle•il paye en €,
* un second flux est créé pour reverser le complément de la cotisation.
*
* @ORM\Entity
*/
class TauxCotisationReversement extends TauxCotisationApplication
class CotisationTavReversement extends CotisationTavApplication
{
/**
* @ORM\OneToOne(targetEntity="Siege")
......
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Child class used to notify adherents by email.
*
* - [Allocation selon foyer] Un administrateur effectue une opération de correction de solde qui conduit à un reversement.
* @ORM\Entity
*/
class CotisationTavReversementCorrectionSolde extends CotisationTavReversement
{
/**
* @return string
*/
public function getType(): string
{
return parent::TYPE_REVERSEMENT_COTISATION_ADHERENT_CORRECTION_SOLDE;
}
public function getUsersToNotify()
{
return [
'destinataires' => [$this->getDestinataire()->getUser()],
];
}
}
<?php
namespace App\Entity;
use App\Repository\PrestataireProductFamilyRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass=DependentChildRepository::class)
* @ORM\Table(name="dependent_child")
*/
class DependentChild
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Adherent::class, inversedBy="adherentDependentChildren")
* @ORM\JoinColumn(nullable=false)
*/
private $adherent;
/**
* @ORM\Column(type="boolean")
*/
private $olderThanFourteen;
/**
* @var float
*
* @ORM\Column(name="sharedcustodypercentage", type="decimal", scale=2, nullable=true)
* @Assert\Type("numeric")
*/
protected $sharedCustodyPercentage;
public function getId(): ?int
{
return $this->id;
}
public function getAdherent(): ?Adherent
{
return $this->adherent;
}
public function setAdherent(?Adherent $adherent): self
{
$this->adherent = $adherent;
return $this;
}
public function getOlderThanFourteen()
{
return $this->olderThanFourteen;
}
public function setOlderThanFourteen($olderThanFourteen)
{
$this->olderThanFourteen = $olderThanFourteen;
return $this;
}
public function getSharedCustodyPercentage()
{
return $this->sharedCustodyPercentage;
}
public function setSharedCustodyPercentage($sharedCustodyPercentage)
{
$this->sharedCustodyPercentage = $sharedCustodyPercentage;
return $this;
}
}
......@@ -54,9 +54,12 @@ use Symfony\Component\Validator\Constraints as Assert;
* "ticket_fix" = "TicketFix",
* "ticket_fix_print" = "TicketFixPrint",
* "ticket_fix_destroy" = "TicketFixDestroy",
* "application_taux_cotisation" = "TauxCotisationApplication",
* "reversement_cotisation_adherent" = "TauxCotisationReversement",
* "prelevement_cotisation_adherent" = "TauxCotisationPrelevement",
* "application_cotisation_tav" = "CotisationTavApplication",
* "reversement_cotisation_adherent" = "CotisationTavReversement",
* "prelevement_cotisation_adherent" = "CotisationTavPrelevement",
* "prelevement_cotisation_adherent_depassement_plafond" = "CotisationTavPrelevementDepassementPlafond",
* "prelevement_cotisation_adherent_correction_solde" = "CotisationTavPrelevementCorrectionSolde",
* "reversement_cotisation_adherent_correction_solde" = "CotisationTavReversementCorrectionSolde",
* })
*/
abstract class Flux implements FluxInterface
......@@ -74,7 +77,7 @@ abstract class Flux implements FluxInterface
const TYPE_VENTE = 'vente';
const TYPE_VENTE_EMLC = 'vente_emlc';
const TYPE_TICKET_FIX = 'ticket_fix';
const TYPE_APPLICATION_TAUX_COTISATION = 'application_taux_cotisation';
const TYPE_APPLICATION_COTISATION_TAV = 'application_cotisation_tav';
/**
* @var \Ramsey\Uuid\UuidInterface
......@@ -649,4 +652,30 @@ abstract class Flux implements FluxInterface
return ($this->getCreatedAt() ? $this->getCreatedAt()->format('d/m/Y H:i') . ' | ' : '') . ucwords($this->getParenttype()) . ' : ' . $this->getExpediteur() . ' => ' . $this->getDestinataire() . ' : ' . $this->getMontant() . '€';
}
public function ssaFriendlyTypeName()
{
$friendlyTypeNames = Flux::ssaUsefulFriendlyTypeNames();
return array_key_exists($this->type,$friendlyTypeNames) ?
$friendlyTypeNames[$this->type]
: $this->type;
}
public static function ssaUsefulFriendlyTypeNames($uniqueValues = false)
{
return [
VenteEmlc::TYPE_VENTE_EMLC_ADHERENT => "Achat de MonA via cotisation" . ($uniqueValues ? " (au comptoir)" : ""),
AchatMonnaie::TYPE_ACHAT_ADHERENT => "Achat de MonA via cotisation" . ($uniqueValues ? " (via Payzen)" : ""),
CotisationTavApplication::TYPE_REVERSEMENT_COTISATION_ADHERENT => "Allocation complémentaire de la caisse",
CotisationTavApplication::TYPE_REVERSEMENT_COTISATION_ADHERENT_CORRECTION_SOLDE => "Allocation complémentaire de la caisse" . ($uniqueValues ? " (correction solde)" : ""),
CotisationTavApplication::TYPE_PRELEVEMENT_COTISATION_ADHERENT => "Réduction de l'allocation",
CotisationTavApplication::TYPE_PRELEVEMENT_COTISATION_ADHERENT_DEPASSEMENT_PLAFOND => "Réduction de l'allocation" . ($uniqueValues ? " (dépassement plafond)" : ""),
CotisationTavApplication::TYPE_PRELEVEMENT_COTISATION_ADHERENT_CORRECTION_SOLDE => "Réduction de l'allocation" . ($uniqueValues ? " (correction solde)" : ""),
Don::TYPE_DON_ADHERENT => Don::TYPE_DON_ADHERENT,
Don::TYPE_DON_PRESTATAIRE => Don::TYPE_DON_PRESTATAIRE,
Reconversion::TYPE_RECONVERSION_PRESTATAIRE => Reconversion::TYPE_RECONVERSION_PRESTATAIRE,
Transaction::TYPE_TRANSACTION_PRESTATAIRE_ADHERENT => Transaction::TYPE_TRANSACTION_PRESTATAIRE_ADHERENT,
Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE => Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE
];
}
}
......@@ -66,6 +66,23 @@ class Geoloc
*/
private $lon;
/**
* @var Subterritory
*
* @ORM\ManyToOne(targetEntity="Subterritory")
* @ORM\JoinColumn(name="subterritory_id", referencedColumnName="id", nullable=true)
*/
private $subterritory;
/**
* @var string|null
*
* @ORM\Column(name="quartier", type="string", length=255, nullable=true)
* @Groups({"read", "write"})
*/
private $quartier;
public function getId()
{
return $this->id;
......@@ -171,6 +188,46 @@ class Geoloc
return $this;
}
/**
* @return Subterritory|null
*/
public function getSubterritory(): ?Subterritory
{
return $this->subterritory;
}
/**
* @param Subterritory|null $subterritory
*
* @return Geoloc
*/
public function setSubterritory(?Subterritory $subterritory): Geoloc
{
$this->subterritory = $subterritory;
return $this;
}
/**
* @return string|null
*/
public function getQuartier(): ?string
{
return $this->quartier;
}
/**
* @param string|null $quartier
*
* @return Geoloc
*/
public function setQuartier(?string $quartier)
{
$this->quartier = $quartier;
return $this;
}
public function __toString(): string
{
return (!empty($this->adresse) ? $this->adresse : '') . ' ' . (!empty($this->cpostal) ? $this->cpostal : '') . ' ' . (!empty($this->ville) ? $this->ville : '');
......
......@@ -51,6 +51,9 @@ class GlobalParameter
const HELLOASSO_URL_COTISATION_ADHERENT = 'HELLOASSO_URL_COTISATION_ADHERENT';
const HELLOASSO_URL_COTISATION_PRESTATAIRE = 'HELLOASSO_URL_COTISATION_PRESTATAIRE';
const CONTACT_FORM_PHONE_NUMBER = 'CONTACT_FORM_PHONE_NUMBER';
const VIREMENT_RECONVERSION_RAISON_GESTIONNAIRE = 'VIREMENT_RECONVERSION_RAISON_GESTIONNAIRE';
const VIREMENT_RECONVERSION_BIC_GESTIONNAIRE = 'VIREMENT_RECONVERSION_BIC_GESTIONNAIRE';
const VIREMENT_RECONVERSION_IBAN_GESTIONNAIRE = 'VIREMENT_RECONVERSION_IBAN_GESTIONNAIRE';
/**
* @var \Ramsey\Uuid\UuidInterface
......
......@@ -2,6 +2,7 @@
namespace App\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Payum\Core\Model\Payment as BasePayment;
use Ramsey\Uuid\Doctrine\UuidGenerator;
......@@ -18,6 +19,7 @@ class Payment extends BasePayment
const TYPE_COTISATION_PRESTA = 'cotisation_presta';
const TYPE_ADHESION = 'adhesion';
const TYPE_PAIEMENT_COTISATION_TAV = 'paiement_cotisation_tav';
const TYPE_PAIEMENT_RECURRENT_COTISATION_TAV = 'paiement_recurrent_cotisation_tav';
/**
* @var \Ramsey\Uuid\UuidInterface
......@@ -53,6 +55,26 @@ class Payment extends BasePayment
protected $extra_data;
/**
* @ORM\Column(type="boolean", nullable=True)
*/
private $isRecurrent;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $recurrenceMonthsCount;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $recurrenceMonthDay;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $recurrenceAmount;
/**
* @return string
*/
public function getStatus(): ?string
......@@ -111,4 +133,106 @@ class Payment extends BasePayment
return $this;
}
public function getIsRecurrent(): ?bool
{
return $this->isRecurrent;
}
public function setIsRecurrent(bool $isRecurrent): self
{
$this->isRecurrent = $isRecurrent;
return $this;
}
public function getRecurrenceMonthsCount(): ?int
{
return $this->recurrenceMonthsCount;
}
public function setRecurrenceMonthsCount(?int $recurrenceMonthsCount): self
{
$this->recurrenceMonthsCount = $recurrenceMonthsCount;
return $this;
}
public function getRecurrenceMonthDay(): ?int
{
return $this->recurrenceMonthDay;
}
public function setRecurrenceMonthDay(?int $recurrenceMonthDay): self
{
$this->recurrenceMonthDay = $recurrenceMonthDay;
return $this;
}
public function getRecurrenceAmount(): ?int
{
return $this->recurrenceAmount;
}
public function setRecurrenceAmount(?int $recurrenceAmount): self
{
$this->recurrenceAmount = $recurrenceAmount;
return $this;
}
/**
* Return null in case of error
* Returns true if payment is already ended or CB expired
* Returns false otherwise
*
* @param $reason
* @return bool|null
*/
public function isRecurringPaymentEndedOrExpired(&$reason)
{
if (
!$this->details
|| !array_key_exists('vads_effective_creation_date', $this->details)
|| !array_key_exists('vads_expiry_year', $this->details)
|| !array_key_exists('vads_effective_creation_date', $this->details)
) {
$reason = "Attribut détails vide ou clés absentes.";
return null;
}
$firstDayOfCreationMonth = DateTime::createFromFormat(
'Ymd',
substr($this->details['vads_effective_creation_date'], 0, 6) . "01"
);
if (!$firstDayOfCreationMonth) {
$reason = "Error parsing vads_effective_creation_date : " . $this->details['vads_effective_creation_date'];
return null;
}
$day = $this->getRecurrenceMonthDay() ? $this->getRecurrenceMonthDay() - 1 : 0; //if not set assume first day of month (not a big deal anyway)
$dateOfFirstOccurenceAfterInitialPayment = $firstDayOfCreationMonth->modify(
"+" . $day . " days next month"
);
$paymentEndDate = $this->getRecurrenceMonthsCount() ?
$dateOfFirstOccurenceAfterInitialPayment->modify(
"+" . ($this->getRecurrenceMonthsCount() - 1) . " months" //minus one because initial payment is not considered by payzen as the first occurence
)
: null; //assume no end date if recurrenceMonthsCount not set
//Now check expiry date
$paymentEndDateDueToExpiry = new DateTime();
$paymentEndDateDueToExpiry->setDate($this->details['vads_expiry_year'], $this->details['vads_expiry_month'], $day + 1);
$now = new DateTime();
if ($paymentEndDate && $paymentEndDate < $paymentEndDateDueToExpiry) {
//Compare now with payment end date
$reason = "Paiement récurrent en cours jusqu'à dernière échéance le " . $paymentEndDate->format('d/m/Y') . ".";
return $now >= $paymentEndDate;
} else {
//Compare now with expiry date
$reason = "Paiement récurrent en cours jusqu'à dernière échéance le " . $paymentEndDateDueToExpiry->format('d/m/Y') . " en raison de l'expiration du moyen de paiement.";
return $now >= $paymentEndDateDueToExpiry;
}
}
}
......@@ -9,6 +9,7 @@ use App\Entity\EntityTrait\HasEcompteEntity;
use App\Flux\AccountableInterface;
use App\Flux\AccountableObject;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Timestampable\Traits\TimestampableEntity;
......@@ -44,6 +45,9 @@ class Prestataire extends AccountableObject implements AccountableInterface
use HasEcompteEntity;
use HasAccountsTrait;
const DISTRIBUTOR = 'distributor';
const PRODUCER = 'producer';
/**
* @var \Ramsey\Uuid\UuidInterface
*
......@@ -142,6 +146,19 @@ class Prestataire extends AccountableObject implements AccountableInterface
private $iban;
/**
* Bank Identifier Code.
*
* @var string
*
* @ORM\Column(name="bic", type="string", nullable=true)
*
* @Assert\Bic(
* ibanPropertyPath="iban"
* )
*/
private $bic;
/**
* @var string
*
* @ORM\Column(name="siret", type="string", length=50, nullable=true)
......@@ -198,6 +215,22 @@ class Prestataire extends AccountableObject implements AccountableInterface
private $horaires;
/**
* @var string|null (champ libre)
*
* @ORM\Column(name="marketchannelfunction", type="text", nullable=true)
* @Groups({"read", "write"})
*/
private $marketchannelfunction;
public function setMarketChannelFunction($var)
{
$this->marketchannelfunction = $var;
}
public function getMarketChannelFunction()
{
return $this->marketchannelfunction;
}
/**
* @var TypePrestataire
*
* @ORM\ManyToOne(targetEntity="TypePrestataire", cascade={"persist"}, inversedBy="prestataires")
......@@ -206,6 +239,21 @@ class Prestataire extends AccountableObject implements AccountableInterface
private $typeprestataire;
/**
* @var SelfEvalPrestaQuiz
* @ORM\OneToOne(targetEntity="SelfEvalPrestaQuiz", cascade={"persist"})
* @ORM\JoinColumn(name="selfevalprestaquiz_id", referencedColumnName="id", nullable=true)
*/
protected $selfevalprestaquiz;
public function setSelfEvalPrestaQuiz($var)
{
$this->selfevalprestaquiz = $var;
}
public function getSelfEvalPrestaQuiz()
{
return $this->selfevalprestaquiz;
}
/**
* @var ArrayCollection|Rubrique[]
* @ORM\ManyToMany(targetEntity="Rubrique", mappedBy="prestataires", cascade={"persist"}, fetch="EXTRA_LAZY")
* @Groups({"read", "write"})
......@@ -291,6 +339,15 @@ class Prestataire extends AccountableObject implements AccountableInterface
protected $tauxreconversion;
/**
* Fréquence de reconversion en cas d'automatisation des reconversions.
*
* @var string
* @ORM\Column(name="reconversionFrequency", type="string", length=50, nullable=true)
* @Groups({"read", "write"})
*/
protected $reconversionFrequency;
/**
* @var ArrayCollection|AccountPrestataire[]
* @ORM\OneToMany(targetEntity="AccountPrestataire", mappedBy="prestataire")
*/
......@@ -308,6 +365,20 @@ class Prestataire extends AccountableObject implements AccountableInterface
*/
private $lastTransactionsExportDatetime;
/**
* @ORM\OneToMany(targetEntity=PrestataireProductFamily::class, mappedBy="prestataire", orphanRemoval=true, cascade={"persist"})
*/
private $prestataireProductFamilies;
/**
* @var float
*
* @ORM\Column(name="conventionnement", type="decimal", scale=2, nullable=true)
* @Assert\Type("numeric")
*/
protected $conventionnement;
public function __construct()
{
$this->users = new ArrayCollection();
......@@ -318,6 +389,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
$this->rubriques = new ArrayCollection();
$this->contacts = new ArrayCollection();
$this->accounts = new ArrayCollection();
$this->prestataireProductFamilies = new ArrayCollection();
}
public function getId()
......@@ -482,6 +554,26 @@ class Prestataire extends AccountableObject implements AccountableInterface
/**
* @return string
*/
public function getBic(): ?string
{
return $this->bic;
}
/**
* @param string $bic
*
* @return Prestataire
*/
public function setBic(?string $bic): self
{
$this->bic = $bic;
return $this;
}
/**
* @return string
*/
public function getSiret(): ?string
{
return $this->siret;
......@@ -748,7 +840,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
*
* @return $this
*/
public function removeUser(User $users)
public function removeUser(User $user)
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
......@@ -790,7 +882,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
*
* @return $this
*/
public function removeCaissier(User $caissiers)
public function removeCaissier(User $caissier)
{
if ($this->caissiers->contains($caissier)) {
$this->caissiers->removeElement($caissier);
......@@ -1073,6 +1165,28 @@ class Prestataire extends AccountableObject implements AccountableInterface
}
/**
* Get reconversionFrequency.
*
* @return
*/
public function getReconversionFrequency(): ?string
{
return $this->reconversionFrequency;
}
/**
* Set reconversionFrequency.
*
* @return $this
*/
public function setReconversionFrequency(?string $reconversionFrequency): self
{
$this->reconversionFrequency = $reconversionFrequency;
return $this;
}
/**
* Get comments.
*
* @return string comments
......@@ -1120,4 +1234,54 @@ class Prestataire extends AccountableObject implements AccountableInterface
return $this;
}
/**
* @return Collection<int, PrestataireProductFamily>
*/
public function getPrestataireProductFamilies(): Collection
{
return $this->prestataireProductFamilies;
}
public function addPrestataireProductFamily(PrestataireProductFamily $prestataireProductFamily): self
{
if (!$this->prestataireProductFamilies->contains($prestataireProductFamily)) {
$this->prestataireProductFamilies[] = $prestataireProductFamily;
$prestataireProductFamily->setPrestataire($this);
}
return $this;
}
public function removePrestataireProductFamily(PrestataireProductFamily $prestataireProductFamily): self
{
if ($this->prestataireProductFamilies->removeElement($prestataireProductFamily)) {
// set the owning side to null (unless already changed)
if ($prestataireProductFamily->getPrestataire() === $this) {
$prestataireProductFamily->setPrestataire(null);
}
}
return $this;
}
/**
* @return mixed
*/
public function getConventionnement()
{
return $this->conventionnement !== null ? number_format($this->conventionnement, 2) : null;
}
/**
* @param mixed $conventionnement
*
* @return Prestataire
*/
public function setConventionnement($conventionnement)
{
$this->conventionnement = $conventionnement;
return $this;
}
}
<?php
namespace App\Entity;
use App\Repository\PrestataireProductFamilyRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity(repositoryClass=PrestataireProductFamilyRepository::class)
* @ORM\Table(name="prestataire_product_family", uniqueConstraints={@ORM\UniqueConstraint(name="prestataireproductfamily", columns={"prestataire_id", "product_family_id"})}) )
* @UniqueEntity(
* fields={"prestataire", "productFamily"},
* errorPath="productFamily",
* message="Famille de produits déjà renseignée, les modifications n'ont pas été enregistrées."
* )
*/
class PrestataireProductFamily
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Prestataire::class, inversedBy="prestataireProductFamilies")
* @ORM\JoinColumn(nullable=false)
*/
private $prestataire;
/**
* @ORM\ManyToOne(targetEntity=ProductFamily::class)
* @ORM\JoinColumn(nullable=false)
*/
private $productFamily;
/**
* Products list as a string. Not related to any kind of product entity.
*
* @ORM\Column(type="text")
*/
private $products;
public function getId(): ?int
{
return $this->id;
}
public function getPrestataire(): ?Prestataire
{
return $this->prestataire;
}
public function setPrestataire(?Prestataire $prestataire): self
{
$this->prestataire = $prestataire;
return $this;
}
public function getProductFamily(): ?ProductFamily
{
return $this->productFamily;
}
public function setProductFamily(?ProductFamily $productFamily): self
{
$this->productFamily = $productFamily;
return $this;
}
public function getProducts(): ?string
{
return $this->products;
}
public function setProducts(string $products): self
{
$this->products = $products;
return $this;
}
}
<?php
namespace App\Entity;
use App\Repository\ProductFamilyRepository;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Doctrine\UuidGenerator;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity(repositoryClass=ProductFamilyRepository::class)
* @UniqueEntity(fields="name", message="Une famille de produits portant ce nom existe déjà.")
*/
class ProductFamily
{
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private $id;
/**
* @ORM\Column(type="string", length=100, unique=true)
*/
private $name;
public function getId()
{
return $this->id;
}
/**
* Get name.
*
* @return string name
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Set name.
*
* @return $this
*/
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
......@@ -125,7 +125,7 @@ class ProfilDeCotisation
}
/**
* setContacts.
* setBeneficiaires.
*
* @param [type] $beneficiaires [description]
*/
......
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Doctrine\UuidGenerator;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity
* @ORM\Table(name="selfevalprestaquiz")
*/
class SelfEvalPrestaQuiz
{
/*
* This class is really just about storing results of a form in a database.
* Attributes are not going to be part of any logic code.
* I opt to set all attributes public to save coding time.
*/
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
* @Groups({"read"})
*/
protected $id;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $accessib_geophy;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $accessib_tempor;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $accessib_edupop;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $accessib_divers;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $accessib_vulner;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $accessib_geophy_comment;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $accessib_tempor_comment;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $accessib_edupop_comment;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $accessib_divers_comment;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $accessib_vulner_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $bienetre_format;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $bienetre_format_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $bienetre_impgou;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $bienetre_impgou_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $bienetre_bienet;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $bienetre_bienet_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $bienetre_recben;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $bienetre_recben_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $transpar_jusrem;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $transpar_jusrem_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $transpar_transp;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $transpar_transp_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $transpar_relpro;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $transpar_relpro_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $disagdur_labels;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $disagdur_labels_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $disagdur_condur;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $disagdur_condur_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_labels;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_labels_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_valbio;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_valbio_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_ecorec;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_ecorec_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_pertes;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_pertes_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_climat;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_climat_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_geneti;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_geneti_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_prosol;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_prosol_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $localite_probru;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $localite_probru_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $localite_protra;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $localite_protra_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $accessib_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $accessib_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $bienetre_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $bienetre_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $transpar_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $transpar_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $disagdur_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $disagdur_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $proagdur_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $proagdur_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $localite_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $localite_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $review_accessib_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $review_accessib_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $review_bienetre_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $review_bienetre_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $review_transpar_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $review_transpar_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $review_disagdur_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $review_disagdur_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $review_proagdur_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $review_proagdur_global_comment;
/**
* @var int|null
* @ORM\Column(type="integer", nullable=true)
*/
public $review_localite_global;
/**
* @var string|null
* @ORM\Column(type="text", nullable=true)
*/
public $review_localite_global_comment;
/**
* As the form been fully submitted?
* @var bool
* @ORM\Column(type="boolean", nullable=false, options={"default" : false})
*/
public $isSubmitted;
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Doctrine\UuidGenerator;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
* @ORM\Table(name="subterritory")
*/
class Subterritory
{
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
protected $id;
/**
* @var string|null
*
* @ORM\Column(type="string", unique=true)
* @Assert\NotBlank
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(?string $name)
{
$this->name = $name;
return $this;
}
}
......@@ -29,6 +29,15 @@ class TransactionAdherentPrestataire extends Transaction
protected $destinataire;
/**
* If the transaction is cancelled, cancellerFlux is the
* opposite TransactionPrestataireAdherent flux.
*
* @ORM\ManyToOne(targetEntity="Flux")
* @ORM\JoinColumn(name="cancellerflux_id", referencedColumnName="id", nullable=true)
*/
protected $cancellerFlux;
/**
* @return string
*/
public function getType(): string
......@@ -43,4 +52,7 @@ class TransactionAdherentPrestataire extends Transaction
'destinataires' => $this->getDestinataire()->getUsers()->toArray(),
];
}
public function setCancellerFlux($var) {$this->cancellerFlux = $var;}
public function getCancellerFlux() {return $this->cancellerFlux;}
}
......@@ -207,6 +207,14 @@ class User extends BaseUser
*/
private $comment;
/**
* This flag is set to false as soon as a user with an enabled prestataire logs in.
*
* @var bool
* @ORM\Column(type="boolean", nullable=false, options={"default" : true})
*/
private bool $beforeFirstLoginWithPrestaEnabled;
public function __construct()
{
parent::__construct();
......@@ -225,6 +233,7 @@ class User extends BaseUser
$this->possiblegroups = new ArrayCollection();
$this->alertemailflux = true;
$this->canValidateAchat = false;
$this->beforeFirstLoginWithPrestaEnabled = true;
$this->createApiKey();
$this->createEmailToken();
}
......@@ -826,4 +835,13 @@ class User extends BaseUser
return $this;
}
public function getBeforeFirstLoginWithPrestaEnabled(): bool
{
return $this->beforeFirstLoginWithPrestaEnabled;
}
public function setBeforeFirstLoginWithPrestaEnabled($var)
{
$this->beforeFirstLoginWithPrestaEnabled = $var;
}
}
<?php
namespace App\Enum;
abstract class ReconversionFrequencyEnum
{
const MOYEN_ONCE_A_MONTH = 'once_a_month';
const MOYEN_TWICE_A_MONTH = 'twice_a_month';
const MOYEN_ONCE_TWO_MONTHS = 'once_every_two_month';
/** @var array user friendly named type */
protected static $typeName = [
self::MOYEN_ONCE_A_MONTH => '1 fois par mois',
self::MOYEN_TWICE_A_MONTH => '2 fois par mois',
self::MOYEN_ONCE_TWO_MONTHS => '1 fois tous les deux mois',
];
/**
* @param string $typeShortName
*
* @return string
*/
public static function getTypeName($typeShortName)
{
if (!isset(static::$typeName[$typeShortName])) {
return "Unknown type ($typeShortName)";
}
return static::$typeName[$typeShortName];
}
/**
* @return array<string>
*/
public static function getAvailableTypes()
{
return [
self::MOYEN_ONCE_A_MONTH,
self::MOYEN_TWICE_A_MONTH,
self::MOYEN_ONCE_TWO_MONTHS,
];
}
}
......@@ -2,37 +2,20 @@
namespace App\EventListener;
use App\Utils\OperationUtils;
use App\Utils\PaymentUtils;
use App\Utils\TAVCotisationUtils;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use Payum\Core\Extension\Context;
use Payum\Core\Extension\ExtensionInterface;
use Payum\Core\Model\PaymentInterface;
use Payum\Core\Request\Generic;
use Payum\Core\Request\GetHumanStatus;
use Payum\Core\Request\GetStatusInterface;
use Payum\Core\Bridge\Symfony\Event\ExecuteEvent;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\SerializerInterface;
use App\Events\MLCEvents;
use App\Events\FluxEvent;
use App\Entity\Flux;
use App\Entity\Payment;
use App\Entity\PaymentToken;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Adherent;
use App\Entity\Prestataire;
use App\Entity\Geoloc;
use App\Entity\Groupe;
use App\Entity\Usergroup;
use App\Entity\AchatMonnaieAdherent;
use App\Entity\AchatMonnaiePrestataire;
use App\Entity\CotisationAdherent;
use App\Entity\CotisationPrestataire;
use App\Entity\Don;
use App\Utils\OperationUtils;
use App\Utils\TAVCotisationUtils;
class PaymentStatusExtension implements ExtensionInterface
{
......@@ -42,6 +25,8 @@ class PaymentStatusExtension implements ExtensionInterface
private $userManager;
private $operationUtils;
private $tavCotisationsUtils;
private $container;
private $paymentUtils;
/**
* PaymentStatusExtension constructor.
......@@ -54,7 +39,9 @@ class PaymentStatusExtension implements ExtensionInterface
SerializerInterface $serializer,
UserManagerInterface $userManager,
OperationUtils $operationUtils,
TAVCotisationUtils $tavCotisationsUtils
TAVCotisationUtils $tavCotisationsUtils,
ContainerInterface $container,
PaymentUtils $paymentUtils
) {
$this->em = $em;
$this->eventDispatcher = $eventDispatcher;
......@@ -62,6 +49,8 @@ class PaymentStatusExtension implements ExtensionInterface
$this->userManager = $userManager;
$this->operationUtils = $operationUtils;
$this->tavCotisationsUtils = $tavCotisationsUtils;
$this->container = $container;
$this->paymentUtils = $paymentUtils;
}
/**
......@@ -91,213 +80,30 @@ class PaymentStatusExtension implements ExtensionInterface
// Get current & new status
$context->getGateway()->execute($status = new GetHumanStatus($payment));
$current_payment_status = $payment->getStatus();
// Payment can be captured if it hasn't been captured before
if ($current_payment_status !== GetHumanStatus::STATUS_CAPTURED
&& $current_payment_status != GetHumanStatus::STATUS_AUTHORIZED)
{
// If payment succesful, persist serialized 'Flux' stored in payment
if ($status->getValue() == GetHumanStatus::STATUS_CAPTURED
|| $status->getValue() == GetHumanStatus::STATUS_AUTHORIZED)
{
$flux_array = json_decode($payment->getFluxData(), true);
$type = $payment->getDescription();
if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
AchatMonnaieAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Adherent::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setReconverti(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($dest);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
AchatMonnaiePrestataire::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setReconverti(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_PRESTATAIRE);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($dest);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_COTISATION_ADHERENT == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
CotisationAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Adherent::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setRecu(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($exp);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_COTISATION_PRESTA == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
CotisationPrestataire::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Prestataire::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setRecu(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_PRESTATAIRE);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($exp);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_ADHESION == $type) {
$new_adherent_data = json_decode($payment->getExtraData());
$adherent = new Adherent();
$user = $this->userManager->createUser();
$usergroup = $this->em->getRepository(Usergroup::class)->findOneByName('Adherent');
$group = $this->em->getRepository(Groupe::class)->findOneBy(array('id' => $new_adherent_data->groupe->id));
$user->setEmail($new_adherent_data->user->email);
$user->setUsername($new_adherent_data->user->username);
$user->setFirstname($new_adherent_data->user->firstname);
$user->setLastname($new_adherent_data->user->lastname);
$user->setPlainPassword($new_adherent_data->user->plainPassword);
$user->setEnabled(true);
$user->addPossiblegroup($usergroup);
$user->addGroup($usergroup);
$user->addRole('ROLE_ADHERENT');
$user->setAdherent($adherent);
$adherent->setEcompte('0');
$adherent->setUser($user);
$adherent->setGroupe($group);
if ($adherent->getGeoloc() == null) {
$geoloc = new Geoloc();
$geoloc->setAdresse($new_adherent_data->geoloc->adresse);
$geoloc->setCpostal($new_adherent_data->geoloc->cpostal);
$geoloc->setVille($new_adherent_data->geoloc->ville);
$adherent->setGeoloc($geoloc);
}
$this->em->persist($adherent);
$this->em->flush();
// Create first cotisation
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
CotisationAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$flux->setOperateur($user);
$flux->setExpediteur($adherent);
$flux->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(array('mlc' => true)));
$flux->setRole('Adherent');
$flux->setRecu(true);
// Update payment with new user id, remove user data
$payment->setClientId($user->getId());
$payment->setExtraData('');
$this->em->persist($payment);
} else if (Payment::TYPE_PAIEMENT_COTISATION_TAV == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
AchatMonnaieAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Adherent::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setReconverti(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($dest);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else {
// Bad request
}
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
if (Payment::TYPE_PAIEMENT_COTISATION_TAV == $type) {
// Apply cotisation rate, create new flux
$this->tavCotisationsUtils->applyTauxCotisation($flux);
}
$current_payment_status = $payment->getStatus();
$new_status = $status->getValue();
if (
GetHumanStatus::STATUS_CAPTURED !== $current_payment_status
&& GetHumanStatus::STATUS_AUTHORIZED != $current_payment_status
) {
if (
GetHumanStatus::STATUS_CAPTURED == $new_status
|| GetHumanStatus::STATUS_AUTHORIZED == $new_status
) {
$this->paymentUtils->handlePayzenNotificationCore($payment);
// Invalidate (delete) notify token after payment is captured
$this->em->remove($token);
$this->em->flush();
}
}
//Update status
// Update payment status with status received in payzen response
$payment->setStatus($status->getValue());
$payment->setStatus($new_status);
//Flush
$this->em->persist($payment);
$this->em->flush();
}
......
......@@ -28,6 +28,7 @@ use App\Entity\User;
use App\Form\Type\AchatMonnaieAConfirmerAdherentFormType;
use App\Form\Type\AchatMonnaieAConfirmerPrestataireFormType;
use App\Form\Type\AchatMonnaieAdherentFormType;
use App\Form\Type\AchatMonnaieAdherentRecurrentFormType;
use App\Form\Type\AchatMonnaiePrestataireFormType;
use App\Form\Type\AdherentInfosFormType;
use App\Form\Type\ChangeAdherentComptoirFormType;
......@@ -474,6 +475,19 @@ class FormFactory
return $form->createView();
}
public function getPaiementRecurrentCotisationTAVForm(User $user)
{
if (empty($user) || !$user->isGranted('ROLE_ADHERENT')) {
throw new \Exception('[FORM 25] Opération impossible !');
}
$entity = new AchatMonnaieAdherent();
$entity->setOperateur($user);
$form = $this->ff->create(AchatMonnaieAdherentRecurrentFormType::class, $entity, ['action' => $this->router->generate('paiementRecurrentCotisTav')]);
return $form->createView();
}
public function getComptoirEncaisserDonAdherentForm(User $user)
{
if (empty($user) || empty($this->session->get('_comptoirgere'))) {
......
<?php
namespace App\Form\Type;
use App\Entity\AchatMonnaieAdherent;
use App\Entity\Adherent;
use App\Entity\DonAdherent;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
class AchatMonnaieAdherentRecurrentFormType extends AchatMonnaieAdherentFormType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$now = new \DateTime();
//force prelevement to occur on same day as today to make sure next prelevement occurs next month
//if today is after the 28th, use the 28th so that prelevement always occurs on same date
$jourPrelevement = min($now->format('d'),28);
$builder
->add('nombreMois', IntegerType::class, [
'label' => 'Nombre d\'échéances désirées : ',
'required' => true,
'mapped' => false,
'constraints' => [
new GreaterThanOrEqual(['value' => 2, 'message' => "Le nombre d'échéances doit être au moins égal à 2."]),
],
'help' => "Une échéance par mois.
Le premier paiement est inclus dans le nombre d'échéances.
Le nombre d'échéance réel pourra être diminué selon la date d'expiration du moyen de paiement utilisé.
Pour demander une interruption anticipée, merci de contacter la caisse.",
'attr' => ['autocomplete' => 'off']
])
->add('jourPrelevement', ChoiceType::class, [
'label' => 'Jour du prélèvement dans le mois : ',
'choices' => [strval($jourPrelevement) => $jourPrelevement],
'data' => $jourPrelevement,
'required' => true,
'mapped' => false,
'attr' => ['autocomplete' => 'off', 'readonly' => true],
'help' => "Pour simplifier le traitement de l'information, le jour de prélèvement est fixé au jour du premier paiement ou au plus tard le 28 du mois."
])
->remove('saveHelloAsso')
->remove('save')
->add('save', SubmitType::class, [
'label' => 'Payer en CB mon premier paiement et créer un paiement récurrent',
'translation_domain' => 'messages',
'attr' => [
'class' => 'btn-primary btn achatCBSubmit',
],
])
->remove('reference')
->add('reference', HiddenType::class, [
'data' => 'Achat monnaie en CB (récurrent) Adhérent',
])
;
$builder
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => AchatMonnaieAdherent::class,
]);
}
public function getParent()
{
return AchatMonnaieAdherentFormType::class;
}
public function getBlockPrefix()
{
return 'formAchatMonnaieAdherentRecurrent';
}
}
......@@ -36,12 +36,23 @@ class AchatMonnaieFormType extends FluxFormType
if ($this->container->getParameter('tav_env')) {
$montant = 0;
if ($this->container->getParameter('household_based_allowance')) {
$cosisationMontant = $this->security->getUser()->getAdherent()->getCotisationAmount();
if (null != $cosisationMontant) {
$montant = $cosisationMontant;
} else {
$montant = false;
}
} else {
$profilDeCotisation = $this->security->getUser()->getAdherent()->getProfilDeCotisation();
if (null != $profilDeCotisation) {
$montant = $profilDeCotisation->getMontant();
} else {
$montant = false;
}
}
$builder
->add('montant', HiddenType::class, [
......
<?php
namespace App\Form\Type;
use App\Entity\DependentChild;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DependentChildFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('olderThanFourteen', ChoiceType::class, array(
'choices' => [
'oui' => true,
'non' => false
],
'label' => "A-t-il plus de 14 ans ?",
'required' => true,
'expanded' => true
))
->add('sharedCustodyPercentage', ChoiceType::class, [
'label' => "Est-il en garde partagée ?",
'required' => true,
'choices' => [
'non' => null,
'oui : je le garde 25 % du temps' => '0.25',//using strings as values (and not floats) seems required
'oui : je le garde 50 % du temps' => '0.50',//to make display work fine
'oui : je le garde 75 % du temps' => '0.75',
],
'required' => false,
'attr' => ['autocomplete' => 'off'] //avoid non-saved value to be displayed
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => DependentChild::class,
]);
}
public function getBlockPrefix()
{
return 'formDependentChild';
}
}
<?php
namespace App\Form\Type;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
class DistributorSelfEvalPrestaQuizType extends SelfEvalPrestaQuizType
{
public function __construct(Security $security, array $options = [])
{
parent::__construct($security, $options);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
/* PARTIE 3 : TRANSPARENCE ET JUSTE REMUNERATION */
$this->opts['label'] = "Juste rémunération : les prix d'achats aux producteurs correspondent-ils au coût de la production (y compris la rémunération) ?";
$this->opts['choices'] = [
$this->frown . " pas connaissance du coût de production des produits achetés" => 0,
$this->meh . " ne correspondent pas toujours mais connaissance du coût de production" => 1,
$this->smile . " oui" => 2,
];
$builder->add('transpar_jusrem', ChoiceType::class, $this->opts);
$builder->add('transpar_jusrem_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Transparence : les consommateurs peuvent-ils accéder à des informations leur permettant de comprendre
comment les prix des produits sont construits ?";
$this->opts['choices'] = [
$this->frown . " aucun moyen d'avoir accès aux informations" => 0,
$this->meh . " au moins via l'équipe en discutant" => 1,
$this->smile . " affichages et en discutant" => 2,
];
$builder->add('transpar_transp', ChoiceType::class, $this->opts);
$builder->add('transpar_transp_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Relation au producteur : sur l'offre alimentaire globale, quels sont les circuits de commercialisation majoritaires ?";
$this->opts['choices'] = [
$this->frown . " plus de 2 intermédiaires connus ou intraçable" => 0,
$this->meh . " 2 intermédiaires connus" => 1,
$this->smile . " 0 ou 1 intermédiaire connu" => 2,
];
$builder->add('transpar_relpro', ChoiceType::class, $this->opts);
$builder->add('transpar_relpro_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Transparence et juste rémunération : " . $this->labelEvalGlob;
$this->opts['choices'] = $this->stdGlobalChoices;
$builder->add('transpar_global', ChoiceType::class, $this->opts);
$builder->add('transpar_global_comment', TextareaType::class, $this->globalCmtOpts);
/* PARTIE 4 : PRATIQUES AGRICOLES DURABLES */
$this->opts['label'] = "Détenez-vous des labels ou des certifications (AB, Bio, Nature et Progrès, système de garantie
participatif ou équivalent minimum ?";
$this->opts['choices'] = [
$this->frown . " moins de 50 % des produits" => 0,
$this->meh . " entre 50 et 70% des produits" => 1,
$this->smile . " plus de 70 % des produits" => 2,
];
$builder->add('disagdur_labels', ChoiceType::class, $this->opts);
$builder->add('disagdur_labels_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Le point de vente s'assure-t-il que ses produits alimentaires non labellisés mais conventionnables sont issus
de pratiques agricoles durables ?";
$this->opts['choices'] = [
$this->frown . " non" => 0,
$this->meh . " oui par des échanges avec le producteur ou le revendeur qui connaît les pratiques agricoles du secteur" => 1,
$this->smile . " oui" => 2,
];
$builder->add('disagdur_condur', ChoiceType::class, $this->opts);
$builder->add('disagdur_condur_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Pratiques agricoles durables : " . $this->labelEvalGlob;
$this->opts['choices'] = $this->stdGlobalChoices;
$builder->add('disagdur_global', ChoiceType::class, $this->opts);
$builder->add('disagdur_global_comment', TextareaType::class, $this->globalCmtOpts);
/* PARTIE 5 : LOCALITE DES PRODUITS */
$this->opts['label'] = "Quelle est la provenance de l'offre alimentaire globale en produits bruts ?";
$this->opts['choices'] = [
$this->frown . " 0 à 49 % de produits locaux (moins de 250 km à la ronde)" => 0,
$this->meh . " 50 à 79 % de produits locaux (moins de 250 km à la ronde)" => 1,
$this->smile . " plus de 80 % de produits locaux (moins de 250 km à la ronde)" => 2,
];
$builder->add('localite_probru', ChoiceType::class, $this->opts);
$builder->add('localite_probru_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Quelle est la provenance de l'offre alimentaire globale en produits transformés ?";
$this->opts['choices'] = [
$this->frown . " 0 à 24 % de produits locaux (moins de 250 km à la ronde)" => 0,
$this->meh . " 25 à 49 % de produits locaux (moins de 250 km à la ronde)" => 1,
$this->smile . " plus de 50 % de produits locaux (moins de 250 km à la ronde)" => 2,
];
$builder->add('localite_protra', ChoiceType::class, $this->opts);
$builder->add('localite_protra_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Localité des produits : " . $this->labelEvalGlob;
$this->opts['choices'] = $this->stdGlobalChoices;
$builder->add('localite_global', ChoiceType::class, $this->opts);
$builder->add('localite_global_comment', TextareaType::class, $this->globalCmtOpts);
//Review
if($options['mode'] !== self::PRESTA_EDIT) {
$this->reviewOpts['label'] = "Transparence et juste rémunération : " . $this->reviewLabel;
$this->reviewOpts['choices'] = $this->stdGlobalChoices;
$builder->add('review_transpar_global', ChoiceType::class, $this->reviewOpts);
$builder->add('review_transpar_global_comment', TextareaType::class, $this->reviewCmtOpts);
$this->reviewOpts['label'] = "Pratiques agricoles durables : " . $this->reviewLabel;
$this->reviewOpts['choices'] = $this->stdGlobalChoices;
$builder->add('review_disagdur_global', ChoiceType::class, $this->reviewOpts);
$builder->add('review_disagdur_global_comment', TextareaType::class, $this->reviewCmtOpts);
$this->reviewOpts['label'] = "Localité des produits : " . $this->reviewLabel;
$this->reviewOpts['choices'] = $this->stdGlobalChoices;
$builder->add('review_localite_global', ChoiceType::class, $this->reviewOpts);
$builder->add('review_localite_global_comment', TextareaType::class, $this->reviewCmtOpts);
}
}
public function getBlockPrefix(): string
{
return 'formDistributorSelfEvalPrestaQuiz';
}
}
......@@ -10,23 +10,29 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\DependencyInjection\ContainerInterface;
use App\Entity\Adherent;
use App\Entity\GlobalParameter;
class EncaissementFormType extends AbstractType
{
protected $em;
protected $security;
protected $container;
public function __construct(
EntityManagerInterface $em,
Security $security
Security $security,
ContainerInterface $container
) {
$this->em = $em;
$this->security = $security;
$this->container = $container;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$mlcName = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL);
$builder
->add('adherent', EntityType::class, [
'class' => Adherent::class,
......@@ -40,7 +46,7 @@ class EncaissementFormType extends AbstractType
'choice_label' => 'name'
])
->add('montant', NumberType::class, [
'label' => 'Montant : ',
'label' => "Montant à payer en {$mlcName} : ",
'required' => true,
'attr' => ['autocomplete' => 'off']
])
......@@ -51,6 +57,20 @@ class EncaissementFormType extends AbstractType
])
->add('save', SubmitType::class, ['label' => 'Valider'])
;
if ($this->container->getParameter('presta_self_init_and_eval')) {
$builder
->add('montantPanier', NumberType::class, [
'label' => 'Montant total du panier : ',
'required' => true,
'attr' => ['autocomplete' => 'off'],
'mapped' => false,
'help' => ''
])
;
}
}
public function getBlockPrefix()
......
......@@ -20,13 +20,17 @@ class EncaisserCotisationAdherentFormType extends VenteEmlcAdherentFormType
// Get the cotisation amount for each adherent. Used for display purposes on the front end.
$adherents = $this->em->getRepository(Adherent::class)->findOrderByName();
$adherentsProfiles = [];
$adherentsCotisationAmounts = [];
foreach ($adherents as $adh) {
$montant = null;
if (!is_null($adh->getProfilDeCotisation()) ) {
if ($this->container->getParameter('household_based_allowance') && !is_null($adh->getCotisationAmount())) {
$montant = $adh->getCotisationAmount();
} else if (!$this->container->getParameter('household_based_allowance') && !is_null($adh->getProfilDeCotisation())) {
$montant = $adh->getProfilDeCotisation()->getMontant();
}
$adherentsProfiles[strval($adh->getId())] = $montant;
$adherentsCotisationAmounts[strval($adh->getId())] = $montant;
}
$builder
......@@ -37,7 +41,7 @@ class EncaisserCotisationAdherentFormType extends VenteEmlcAdherentFormType
'data' => 0
])
->add('cotisationMontants', HiddenType::class, [
'data' => json_encode($adherentsProfiles),
'data' => json_encode($adherentsCotisationAmounts),
'mapped' => false
])
;
......
......@@ -3,6 +3,8 @@
namespace App\Form\Type;
use App\Entity\Geoloc;
use App\Entity\Subterritory;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
......@@ -71,6 +73,25 @@ class GeolocFormType extends AbstractType
])
;
}
if (true === $options['with_quartier']) {
$builder
->add('quartier', TextType::class, [
'required' => false,
])
;
}
if (true === $options['with_subterritory']) {
$builder
->add('subterritory', EntityType::class, [
'class' => Subterritory::class,
'label' => 'Territoire',
'required' => true,
'choice_label' => 'name',
'placeholder' => "Choix du territoire",
'attr' => ['autocomplete' => 'off']
])
;
}
}
public function configureOptions(OptionsResolver $resolver)
......@@ -80,6 +101,8 @@ class GeolocFormType extends AbstractType
'data_class' => Geoloc::class,
'with_geoloc' => true,
'with_latlon' => true,
'with_subterritory' => false,
'with_quartier' => false
]);
}
......
......@@ -331,6 +331,27 @@ class GlobalConfigurationFormType extends AbstractType
'required' => false,
'_placeholder' => '',
])
->add('raisongestionnairevirementreconversion', GlobalParameterType::class, [
'label' => 'Raison du gestionnaire pour les virements de reconversion automatisés',
'_description' => 'Raison du gestionnaire pour les virements de reconversion automatisés',
'name_param' => GlobalParameter::VIREMENT_RECONVERSION_RAISON_GESTIONNAIRE,
'required' => false,
'_placeholder' => '',
])
->add('bicgestionnairevirementreconversion', GlobalParameterType::class, [
'label' => 'BIC du gestionnaire pour les virements de reconversion automatisés',
'_description' => 'BIC du gestionnaire pour les virements de reconversion automatisés',
'name_param' => GlobalParameter::VIREMENT_RECONVERSION_BIC_GESTIONNAIRE,
'required' => false,
'_placeholder' => '',
])
->add('ibangestionnairevirementreconversion', GlobalParameterType::class, [
'label' => 'IBAN du gestionnaire pour les virements de reconversion automatisés',
'_description' => 'IBAN du gestionnaire pour les virements de reconversion automatisés',
'name_param' => GlobalParameter::VIREMENT_RECONVERSION_IBAN_GESTIONNAIRE,
'required' => false,
'_placeholder' => '',
])
;
}
......
<?php
namespace App\Form\Type;
use App\Entity\Prestataire;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotNull;
/**
* Identification questionnaire for prestataire.
*/
class InfosPrestaQuizType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('userFirstName', TextType::class, [
'mapped' => false, //store it into the user
'label' => 'Prénom',
'required' => true,
'constraints' => [new NotNull()],
])
->add('userLastName', TextType::class, [
'mapped' => false, //store it into the user
'label' => 'Nom',
'required' => true,
'constraints' => [new NotNull()],
])
->add('raison', TextType::class, [
'required' => true,
'constraints' => [new NotNull()],
])
->add('responsable', TextType::class, [
'required' => true,
'constraints' => [new NotNull()],
'label' => 'Nom du responsable'
])
->add('metier', TextType::class, [
'label' => 'Fonction du responsable',
'required' => true,
'constraints' => [new NotNull()],
])
->add('horaires', TextType::class, [
'label' => "Jours et horaires d'ouverture :",
'required' => true,
'constraints' => [new NotNull()],
])
->add('geolocs', CollectionType::class, [
'entry_type' => GeolocPrestataireFormType::class,
'entry_options' => ['label' => false, 'with_latlon' => false],
'required' => true,
'allow_add' => false,
'allow_delete' => false,
'by_reference' => false,
'label' => false,
])
->add('marketchannelfunction', ChoiceType::class, [
'label' => 'Fonction dans le circuit de distribution',
'choices' => [
'Distributeur/distributrice' => Prestataire::DISTRIBUTOR,
'Producteur/productrice' => Prestataire::PRODUCER,
],
'required' => true,
'constraints' => [new NotNull()],
])
->add('save', SubmitType::class, ['label' => 'Envoyer'])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Prestataire::class,
]);
}
public function getBlockPrefix(): string
{
return 'formInfosPrestaQuiz';
}
}
......@@ -4,7 +4,10 @@ namespace App\Form\Type;
use App\Application\Sonata\MediaBundle\Entity\Media;
use App\Entity\Prestataire;
use App\Entity\PrestataireProductFamily;
use App\Form\Type\PrestataireProductFamilyFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Sonata\MediaBundle\Form\Type\MediaType;
use Symfony\Component\Form\AbstractType;
......@@ -13,16 +16,21 @@ use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Enum\ReconversionFrequencyEnum;
use App\Entity\GlobalParameter;
class PrestataireInfosFormType extends AbstractType
{
protected $em;
protected $container;
public function __construct(EntityManagerInterface $em)
public function __construct(EntityManagerInterface $em, ContainerInterface $container)
{
$this->em = $em;
$this->container = $container;
}
public function buildForm(FormBuilderInterface $builder, array $options)
......@@ -58,6 +66,11 @@ class PrestataireInfosFormType extends AbstractType
'label' => 'SIRET :',
'required' => false,
])
//bic is new field in kohinos-ssa (I think it's OK to add it for non-tav env as well)
->add('bic', TextType::class, [
'label' => 'BIC :',
'required' => false,
])
->add('iban', TextType::class, [
'label' => 'IBAN :',
'required' => false,
......@@ -77,7 +90,47 @@ class PrestataireInfosFormType extends AbstractType
->add('web', UrlType::class, [
'label' => 'Site web :',
'required' => false,
])
]);
if ($this->container->getParameter('tav_env') && $this->container->getParameter('automatisation_reconversion')) {
$mlcName = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL);
$helpMsqg = "Fréquence à laquelle je souhaite que la caisse commune de l'alimentation (via l'association Acclimat'action)"
. " me verse en euros la somme des {$mlcName} encaissées dans mon point de vente "
. "(1 {$mlcName} = 1 euro). Le versement se fait via un virement bancaire.";
$builder
->add('reconversionFrequency', ChoiceType::class, [
'choices' => ReconversionFrequencyEnum::getAvailableTypes(),
'choice_label' => function ($choice) {
return ReconversionFrequencyEnum::getTypeName($choice);
},
'expanded' => false,
'multiple' => false,
'label' => 'Fréquence de reconversion :',
'placeholder' => 'Choisir une option',
'required' => false,
'help' => $helpMsqg
]);
}
if ($this->container->getParameter('tav_env') && $this->container->getParameter('presta_extra_data')) {
$builder
->add('prestataireProductFamilies', CollectionType::class, [
'entry_type' => PrestataireProductFamilyFormType::class,
'required' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'constraints' => new \Symfony\Component\Validator\Constraints\Valid(),
'label' => "Produits vendus, regroupés par famille ",
'error_mapping' => [
'productFamily' => 'prestataireProductFamilies',
],
]);
}
$builder
->add('description', CKEditorType::class, [
'label' => 'Description :',
'required' => false,
......
<?php
namespace App\Form\Type;
use App\Entity\PrestataireProductFamily;
use App\Entity\ProductFamily;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class PrestataireProductFamilyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('productFamily', EntityType::class, array(
'class' => ProductFamily::class,
'choice_label' => 'name',
'label' => false,
'required' => true,
'placeholder' => 'Choisissez une famille de produits',
'attr' => ['class' => 'prestataire-product-families-select']
))
->add('products', TextareaType::class, [
'label' => false,
'required' => true,
'attr' => [
'class' => 'prestataire-product-families-products',
'placeholder' => 'Renseignez des produits correspondant à cette famille'
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PrestataireProductFamily::class,
]);
}
public function getBlockPrefix()
{
return 'formPrestataireProductFamily';
}
}
<?php
namespace App\Form\Type;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
class ProducerSelfEvalPrestaQuizType extends SelfEvalPrestaQuizType
{
public function __construct(Security $security, array $options = [])
{
parent::__construct($security, $options);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
/* PARTIE 4 : PRATIQUES AGRICOLES DURABLES */
$this->opts['label'] = "Détenez-vous des labels ou des certifications (AB Bio, Nature et Progrès, système de garantie participatif ou équivalent minimum) ?";
$this->opts['choices'] = [
$this->frown . " non" => 0,
$this->smile . " oui" => 2,
];
$builder->add('proagdur_labels', ChoiceType::class, $this->opts);
$builder->add('proagdur_labels_comment', TextareaType::class, $this->cmtOpts);
//Jusqu'à la fin
$this->opts['choices'] = $this->stdChoices;
$this->opts['label'] = "Les pratiques agricoles permettent-elles de valoriser la biodiversité ?";
$builder->add('proagdur_valbio', ChoiceType::class, $this->opts);
$builder->add('proagdur_valbio_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Les pratiques agricoles permettent-elles l'économie ou le recyclage de l'énergie, de l'eau, de la matière organique, des nutriments ?";
$builder->add('proagdur_ecorec', ChoiceType::class, $this->opts);
$builder->add('proagdur_ecorec_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Les pratiques agricoles réduisent-elles les pertes agricoles (au champ ou post-récolte) ?";
$builder->add('proagdur_pertes', ChoiceType::class, $this->opts);
$builder->add('proagdur_pertes_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Les pratiques agricoles contribuent-elles à l'atténuation ou à l'adaptation au changement climatique ?";
$builder->add('proagdur_climat', ChoiceType::class, $this->opts);
$builder->add('proagdur_climat_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Les pratiques agricoles permettent-elles de renforcer l'autonomie de la ferme en ressources génétiques (végétales ou animales), en eau, en intrants, en énergie ?";
$builder->add('proagdur_geneti', ChoiceType::class, $this->opts);
$builder->add('proagdur_geneti_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Les pratiques agricoles permettent-elles la protection des sols ?";
$builder->add('proagdur_prosol', ChoiceType::class, $this->opts);
$builder->add('proagdur_prosol_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Pratiques agricoles durables : " . $this->labelEvalGlob . " (cochez vert si vous détenez un label ou une certification)";
$this->opts['choices'] = $this->stdGlobalChoices;
$builder->add('proagdur_global', ChoiceType::class, $this->opts);
$builder->add('proagdur_global_comment', TextareaType::class, $this->globalCmtOpts);
//Review
if($options['mode'] !== self::PRESTA_EDIT) {
$this->reviewOpts['label'] = "Pratiques agricoles durables : " . $this->reviewLabel;
$this->reviewOpts['choices'] = $this->stdGlobalChoices;
$builder->add('review_proagdur_global', ChoiceType::class, $this->reviewOpts);
$builder->add('review_proagdur_global_comment', TextareaType::class, $this->reviewCmtOpts);
}
}
public function getBlockPrefix(): string
{
return 'formProducerSelfEvalPrestaQuiz';
}
}
<?php
namespace App\Form\Type;
use App\Entity\SelfEvalPrestaQuiz;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
/**
*
* Self-eval prestataire questionnaire base class
* containing useful attributes and buildForm for
* sections that are common to distributors and producers
*/
class SelfEvalPrestaQuizType extends AbstractType
{
const PRESTA_EDIT = 'presta_edit';
const ADMIN_EDIT = 'admin_edit';
const READONLY = 'readonly';
const PRESTA_EDIT_AFTER_DEFINITIVE_SUBMISSION = 'presta_edit_after_definitive_submission';
protected $security;
protected array $stdChoices;
protected array $opts;
protected array $reviewOpts;
protected array $cmtOpts;
protected array $globalCmtOpts;
protected array $reviewCmtOpts;
protected string $labelEvalGlob;
protected string $reviewLabel;
protected string $frown;
protected string $meh;
protected string $smile;
public function __construct(Security $security, array $options = [])
{
$this->security = $security;
$this->frown = '<i class="fas fa-frown text-danger"></i>';
$this->meh = '<i class="fas fa-meh text-warning"></i>';
$this->smile = '<i class="fas fa-smile text-success"></i>';
$this->stdChoices = [
$this->frown . ' non' => 0,
$this->meh . ' peut être amélioré' => 1,
$this->smile . ' oui' => 2,
];
$this->stdGlobalChoices = [
$this->frown => 0,
$this->meh => 1,
$this->smile => 2,
];
$this->labelEvalGlob = "évaluation globale";
$this->reviewLabel = "évaluation du gestionnaire";
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
//options are moved to buildForm as 'mode' option is required to set them up
$this->reviewOpts = [
'label' => '',
'choices' => $this->stdChoices,
'expanded' => true,
"multiple" => false,
'disabled' => $options['mode'] !== self::ADMIN_EDIT
];
$this->opts = $this->reviewOpts;
$this->opts['disabled'] = $options['mode'] !== self::PRESTA_EDIT && $options['mode'] !== self::PRESTA_EDIT_AFTER_DEFINITIVE_SUBMISSION;
$this->reviewCmtOpts = [
'attr' => [
'placeholder' => $options['mode'] !== self::ADMIN_EDIT ? '' : 'Commentaires'
],
'label' => false,
'required' => false,
'disabled' => $options['mode'] !== self::ADMIN_EDIT
];
$this->cmtOpts = $this->reviewCmtOpts;
$this->cmtOpts['disabled'] = $options['mode'] !== self::PRESTA_EDIT && $options['mode'] !== self::PRESTA_EDIT_AFTER_DEFINITIVE_SUBMISSION;
$this->cmtOpts['attr']['placeholder'] = $options['mode'] !== self::PRESTA_EDIT ? '' : 'Commentaires';
$this->globalCmtOpts = $this->cmtOpts;
$this->globalCmtOpts['required'] = true;
$this->globalCmtOpts['attr']['placeholder'] = $options['mode'] !== self::PRESTA_EDIT && $options['mode'] !== self::PRESTA_EDIT_AFTER_DEFINITIVE_SUBMISSION ? '' : 'Commentaires (obligatoire)';
$builder->add($options["mode"],HiddenType::class,['mapped' => false]); //ease conditionnal display in twig
/* PARTIE 1 : ACCESSIBILITE ET INCLUSIVITE */
$this->opts['label'] = "Géographique et physique : le point de vente est-il accessible par différents modes de transport ?";
$this->opts['choices'] = [
$this->frown . " 0 mode de transport" => 0,
$this->meh . " 1 mode de transport" => 1,
$this->smile . " plus d'1 mode de transport" => 2,
];
$builder->add('accessib_geophy', ChoiceType::class, $this->opts);
$builder->add('accessib_geophy_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Temporelle : le point de vente communique-t-il sur ses plages d'ouvertures ?";
$this->opts['choices'] = [
$this->frown . " 0 mode de communication" => 0,
$this->meh . " 1 mode de communication" => 1,
$this->smile . " plus d'1 mode de communication" => 2,
];
$builder->add('accessib_tempor', ChoiceType::class, $this->opts);
$builder->add('accessib_tempor_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Sociale et inclusivité (1/3) : le point de vente mène-t-il des actions de sensibilisation et d'éducation populaire à l'alimentation ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('accessib_edupop', ChoiceType::class, $this->opts);
$builder->add('accessib_edupop_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Sociale et inclusivité (2/3) : le point de vente propose-t-il des services spécifiques pour faciliter son accessibilité à une diversité
de profils (pour les personnes agées, les personnes en situation de handicap, les familles ...) ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('accessib_divers', ChoiceType::class, $this->opts);
$builder->add('accessib_divers_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Sociale et inclusivité (3/3) : le point de vente mène-t-il des actions spécifiques pour lutter contre la vulnérabilité alimentaire ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('accessib_vulner', ChoiceType::class, $this->opts);
$builder->add('accessib_vulner_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Accessibilité et inclusivité : " . $this->labelEvalGlob;
$this->opts['choices'] = $this->stdGlobalChoices;
$builder->add('accessib_global', ChoiceType::class, $this->opts);
$builder->add('accessib_global_comment', TextareaType::class, $this->globalCmtOpts);
/* PARTIE 2 : BIEN-ETRE AU TRAVAIL */
$this->opts['label'] = "Le point de vente met-il en place des actions pour faciliter l'accueil, l'intégration et la formation des personnes qui y travaillent ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('bienetre_format', ChoiceType::class, $this->opts);
$builder->add('bienetre_format_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Les travailleurs sont-ils impliqués dans la gouvernance ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('bienetre_impgou', ChoiceType::class, $this->opts);
$builder->add('bienetre_impgou_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Le point de vente met-il en place des actions pour améliorer le bien-être des personnes qui y travaillent ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('bienetre_bienet', ChoiceType::class, $this->opts);
$builder->add('bienetre_bienet_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Le point de vente met-il en place des actions pour valoriser et reconnaître le travail fourni par les bénévoles ?";
$this->opts['choices'] = $this->stdChoices;
$builder->add('bienetre_recben', ChoiceType::class, $this->opts);
$builder->add('bienetre_recben_comment', TextareaType::class, $this->cmtOpts);
$this->opts['label'] = "Bien-être au travail : " . $this->labelEvalGlob;
$this->opts['choices'] = $this->stdGlobalChoices;
$builder->add('bienetre_global', ChoiceType::class, $this->opts);
$builder->add('bienetre_global_comment', TextareaType::class, $this->globalCmtOpts);
//Review
if($options['mode'] !== self::PRESTA_EDIT) {
$this->reviewOpts['label'] = "Accessibilité et inclusivité : " . $this->reviewLabel;
$this->reviewOpts['choices'] = $this->stdGlobalChoices;
$builder->add('review_accessib_global', ChoiceType::class, $this->reviewOpts);
$builder->add('review_accessib_global_comment', TextareaType::class, $this->reviewCmtOpts);
$this->reviewOpts['label'] = "Bien-être au travail : " . $this->reviewLabel;
$this->reviewOpts['choices'] = $this->stdGlobalChoices;
$builder->add('review_bienetre_global', ChoiceType::class, $this->reviewOpts);
$builder->add('review_bienetre_global_comment', TextareaType::class, $this->reviewCmtOpts);
}
if($options['mode'] !== self::READONLY) {
$builder->add('submit_presta_quizz', HiddenType::class, [
"mapped" => false,
"data" => 1,
"empty_data" => 1
]);
$builder->add('save', SubmitType::class, ['label' => $options['mode'] === self::PRESTA_EDIT ? 'Envoyer' : 'Enregistrer']);
}
if($options['mode'] == self::PRESTA_EDIT) {
$builder->add('temporary_save', SubmitType::class, ['label' => 'Sauvegarder afin de revenir plus tard']);
}
}
/**
*
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SelfEvalPrestaQuiz::class,
'mode' => self::PRESTA_EDIT
]);
}
public function getBlockPrefix(): string
{
return 'formSelfEvalPrestaQuiz';
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240214104305 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE selfevalprestaquiz (id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', accessib_geophy INT NOT NULL, accessib_tempor INT NOT NULL, accessib_edupop INT NOT NULL, accessib_divers INT NOT NULL, accessib_vulner INT NOT NULL, accessib_geophy_comment LONGTEXT NOT NULL, accessib_tempor_comment LONGTEXT NOT NULL, accessib_edupop_comment LONGTEXT NOT NULL, accessib_divers_comment LONGTEXT NOT NULL, accessib_vulner_comment LONGTEXT NOT NULL, bienetre_format INT NOT NULL, bienetre_format_comment LONGTEXT NOT NULL, bienetre_impgou INT NOT NULL, bienetre_impgou_comment LONGTEXT NOT NULL, bienetre_bienet INT NOT NULL, bienetre_bienet_comment LONGTEXT NOT NULL, bienetre_recben INT NOT NULL, bienetre_recben_comment LONGTEXT NOT NULL, transpar_jusrem INT NOT NULL, transpar_jusrem_comment LONGTEXT NOT NULL, transpar_transp INT NOT NULL, transpar_transp_comment LONGTEXT NOT NULL, transpar_relpro INT NOT NULL, transpar_relpro_comment LONGTEXT NOT NULL, disagdur_labels INT NOT NULL, disagdur_labels_comment LONGTEXT NOT NULL, disagdur_condur INT NOT NULL, disagdur_condur_comment LONGTEXT NOT NULL, proagdur_labels INT NOT NULL, proagdur_labels_comment LONGTEXT NOT NULL, proagdur_valbio INT NOT NULL, proagdur_valbio_comment LONGTEXT NOT NULL, proagdur_ecorec INT NOT NULL, proagdur_ecorec_comment LONGTEXT NOT NULL, proagdur_pertes INT NOT NULL, proagdur_pertes_comment LONGTEXT NOT NULL, proagdur_climat INT NOT NULL, proagdur_climat_comment LONGTEXT NOT NULL, proagdur_geneti INT NOT NULL, proagdur_geneti_comment LONGTEXT NOT NULL, proagdur_prosol INT NOT NULL, proagdur_prosol_comment LONGTEXT NOT NULL, localite_probru INT NOT NULL, localite_probru_comment LONGTEXT NOT NULL, localite_protra INT NOT NULL, localite_protra_comment LONGTEXT NOT NULL, accessib_global INT NOT NULL, accessib_global_comment LONGTEXT NOT NULL, bienetre_global INT NOT NULL, bienetre_global_comment LONGTEXT NOT NULL, transpar_global INT NOT NULL, transpar_global_comment LONGTEXT NOT NULL, disagdur_global INT NOT NULL, disagdur_global_comment LONGTEXT NOT NULL, proagdur_global INT NOT NULL, proagdur_global_comment LONGTEXT NOT NULL, localite_global INT NOT NULL, localite_global_comment LONGTEXT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE prestataire ADD selfevalprestaquiz_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\', ADD marketchannelfunction LONGTEXT DEFAULT NULL, CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE prestataire ADD CONSTRAINT FK_60A26480C5DC59A8 FOREIGN KEY (selfevalprestaquiz_id) REFERENCES selfevalprestaquiz (id)');
$this->addSql('CREATE UNIQUE INDEX UNIQ_60A26480C5DC59A8 ON prestataire (selfevalprestaquiz_id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire DROP FOREIGN KEY FK_60A26480C5DC59A8');
$this->addSql('DROP TABLE selfevalprestaquiz');
$this->addSql('DROP INDEX UNIQ_60A26480C5DC59A8 ON prestataire');
$this->addSql('ALTER TABLE prestataire DROP selfevalprestaquiz_id, DROP marketchannelfunction, CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240214152548 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE selfevalprestaquiz CHANGE accessib_geophy_comment accessib_geophy_comment LONGTEXT DEFAULT NULL, CHANGE accessib_tempor_comment accessib_tempor_comment LONGTEXT DEFAULT NULL, CHANGE accessib_edupop_comment accessib_edupop_comment LONGTEXT DEFAULT NULL, CHANGE accessib_divers_comment accessib_divers_comment LONGTEXT DEFAULT NULL, CHANGE accessib_vulner_comment accessib_vulner_comment LONGTEXT DEFAULT NULL, CHANGE bienetre_format_comment bienetre_format_comment LONGTEXT DEFAULT NULL, CHANGE bienetre_impgou_comment bienetre_impgou_comment LONGTEXT DEFAULT NULL, CHANGE bienetre_bienet_comment bienetre_bienet_comment LONGTEXT DEFAULT NULL, CHANGE bienetre_recben_comment bienetre_recben_comment LONGTEXT DEFAULT NULL, CHANGE transpar_jusrem_comment transpar_jusrem_comment LONGTEXT DEFAULT NULL, CHANGE transpar_transp_comment transpar_transp_comment LONGTEXT DEFAULT NULL, CHANGE transpar_relpro_comment transpar_relpro_comment LONGTEXT DEFAULT NULL, CHANGE disagdur_labels_comment disagdur_labels_comment LONGTEXT DEFAULT NULL, CHANGE disagdur_condur_comment disagdur_condur_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_labels_comment proagdur_labels_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_valbio_comment proagdur_valbio_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_ecorec_comment proagdur_ecorec_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_pertes_comment proagdur_pertes_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_climat_comment proagdur_climat_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_geneti_comment proagdur_geneti_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_prosol_comment proagdur_prosol_comment LONGTEXT DEFAULT NULL, CHANGE localite_probru_comment localite_probru_comment LONGTEXT DEFAULT NULL, CHANGE localite_protra_comment localite_protra_comment LONGTEXT DEFAULT NULL, CHANGE accessib_global_comment accessib_global_comment LONGTEXT DEFAULT NULL, CHANGE bienetre_global_comment bienetre_global_comment LONGTEXT DEFAULT NULL, CHANGE transpar_global_comment transpar_global_comment LONGTEXT DEFAULT NULL, CHANGE disagdur_global_comment disagdur_global_comment LONGTEXT DEFAULT NULL, CHANGE proagdur_global_comment proagdur_global_comment LONGTEXT DEFAULT NULL, CHANGE localite_global_comment localite_global_comment LONGTEXT DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE selfevalprestaquiz CHANGE accessib_geophy_comment accessib_geophy_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE accessib_tempor_comment accessib_tempor_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE accessib_edupop_comment accessib_edupop_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE accessib_divers_comment accessib_divers_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE accessib_vulner_comment accessib_vulner_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE bienetre_format_comment bienetre_format_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE bienetre_impgou_comment bienetre_impgou_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE bienetre_bienet_comment bienetre_bienet_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE bienetre_recben_comment bienetre_recben_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE transpar_jusrem_comment transpar_jusrem_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE transpar_transp_comment transpar_transp_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE transpar_relpro_comment transpar_relpro_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE disagdur_labels_comment disagdur_labels_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE disagdur_condur_comment disagdur_condur_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_labels_comment proagdur_labels_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_valbio_comment proagdur_valbio_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_ecorec_comment proagdur_ecorec_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_pertes_comment proagdur_pertes_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_climat_comment proagdur_climat_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_geneti_comment proagdur_geneti_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_prosol_comment proagdur_prosol_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE localite_probru_comment localite_probru_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE localite_protra_comment localite_protra_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE accessib_global_comment accessib_global_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE bienetre_global_comment bienetre_global_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE transpar_global_comment transpar_global_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE disagdur_global_comment disagdur_global_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE proagdur_global_comment proagdur_global_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`, CHANGE localite_global_comment localite_global_comment LONGTEXT CHARACTER SET utf8 NOT NULL COLLATE `utf8_general_ci`');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240214152700 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE selfevalprestaquiz CHANGE accessib_geophy accessib_geophy INT DEFAULT NULL, CHANGE accessib_tempor accessib_tempor INT DEFAULT NULL, CHANGE accessib_edupop accessib_edupop INT DEFAULT NULL, CHANGE accessib_divers accessib_divers INT DEFAULT NULL, CHANGE accessib_vulner accessib_vulner INT DEFAULT NULL, CHANGE bienetre_format bienetre_format INT DEFAULT NULL, CHANGE bienetre_impgou bienetre_impgou INT DEFAULT NULL, CHANGE bienetre_bienet bienetre_bienet INT DEFAULT NULL, CHANGE bienetre_recben bienetre_recben INT DEFAULT NULL, CHANGE transpar_jusrem transpar_jusrem INT DEFAULT NULL, CHANGE transpar_transp transpar_transp INT DEFAULT NULL, CHANGE transpar_relpro transpar_relpro INT DEFAULT NULL, CHANGE disagdur_labels disagdur_labels INT DEFAULT NULL, CHANGE disagdur_condur disagdur_condur INT DEFAULT NULL, CHANGE proagdur_labels proagdur_labels INT DEFAULT NULL, CHANGE proagdur_valbio proagdur_valbio INT DEFAULT NULL, CHANGE proagdur_ecorec proagdur_ecorec INT DEFAULT NULL, CHANGE proagdur_pertes proagdur_pertes INT DEFAULT NULL, CHANGE proagdur_climat proagdur_climat INT DEFAULT NULL, CHANGE proagdur_geneti proagdur_geneti INT DEFAULT NULL, CHANGE proagdur_prosol proagdur_prosol INT DEFAULT NULL, CHANGE localite_probru localite_probru INT DEFAULT NULL, CHANGE localite_protra localite_protra INT DEFAULT NULL, CHANGE accessib_global accessib_global INT DEFAULT NULL, CHANGE bienetre_global bienetre_global INT DEFAULT NULL, CHANGE transpar_global transpar_global INT DEFAULT NULL, CHANGE disagdur_global disagdur_global INT DEFAULT NULL, CHANGE proagdur_global proagdur_global INT DEFAULT NULL, CHANGE localite_global localite_global INT DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE selfevalprestaquiz CHANGE accessib_geophy accessib_geophy INT NOT NULL, CHANGE accessib_tempor accessib_tempor INT NOT NULL, CHANGE accessib_edupop accessib_edupop INT NOT NULL, CHANGE accessib_divers accessib_divers INT NOT NULL, CHANGE accessib_vulner accessib_vulner INT NOT NULL, CHANGE bienetre_format bienetre_format INT NOT NULL, CHANGE bienetre_impgou bienetre_impgou INT NOT NULL, CHANGE bienetre_bienet bienetre_bienet INT NOT NULL, CHANGE bienetre_recben bienetre_recben INT NOT NULL, CHANGE transpar_jusrem transpar_jusrem INT NOT NULL, CHANGE transpar_transp transpar_transp INT NOT NULL, CHANGE transpar_relpro transpar_relpro INT NOT NULL, CHANGE disagdur_labels disagdur_labels INT NOT NULL, CHANGE disagdur_condur disagdur_condur INT NOT NULL, CHANGE proagdur_labels proagdur_labels INT NOT NULL, CHANGE proagdur_valbio proagdur_valbio INT NOT NULL, CHANGE proagdur_ecorec proagdur_ecorec INT NOT NULL, CHANGE proagdur_pertes proagdur_pertes INT NOT NULL, CHANGE proagdur_climat proagdur_climat INT NOT NULL, CHANGE proagdur_geneti proagdur_geneti INT NOT NULL, CHANGE proagdur_prosol proagdur_prosol INT NOT NULL, CHANGE localite_probru localite_probru INT NOT NULL, CHANGE localite_protra localite_protra INT NOT NULL, CHANGE accessib_global accessib_global INT NOT NULL, CHANGE bienetre_global bienetre_global INT NOT NULL, CHANGE transpar_global transpar_global INT NOT NULL, CHANGE disagdur_global disagdur_global INT NOT NULL, CHANGE proagdur_global proagdur_global INT NOT NULL, CHANGE localite_global localite_global INT NOT NULL');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240215192514 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE selfevalprestaquiz ADD review_accessib_global INT DEFAULT NULL, ADD review_accessib_global_comment LONGTEXT DEFAULT NULL, ADD review_bienetre_global INT DEFAULT NULL, ADD review_bienetre_global_comment LONGTEXT DEFAULT NULL, ADD review_transpar_global INT DEFAULT NULL, ADD review_transpar_global_comment LONGTEXT DEFAULT NULL, ADD review_disagdur_global INT DEFAULT NULL, ADD review_disagdur_global_comment LONGTEXT DEFAULT NULL, ADD review_proagdur_global INT DEFAULT NULL, ADD review_proagdur_global_comment LONGTEXT DEFAULT NULL, ADD review_localite_global INT DEFAULT NULL, ADD review_localite_global_comment LONGTEXT DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE selfevalprestaquiz DROP review_accessib_global, DROP review_accessib_global_comment, DROP review_bienetre_global, DROP review_bienetre_global_comment, DROP review_transpar_global, DROP review_transpar_global_comment, DROP review_disagdur_global, DROP review_disagdur_global_comment, DROP review_proagdur_global, DROP review_proagdur_global_comment, DROP review_localite_global, DROP review_localite_global_comment');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240216095500 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire ADD reconversionFrequency VARCHAR(50) DEFAULT NULL, CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire DROP reconversionFrequency, CHANGE iban iban LONGTEXT CHARACTER SET utf8mb3 DEFAULT NULL COLLATE `utf8mb3_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240216140320 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE product_family (id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', name VARCHAR(100) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE product_family');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8mb3 DEFAULT NULL COLLATE `utf8mb3_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240219140409 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('CREATE UNIQUE INDEX UNIQ_C79A60FF5E237E06 ON product_family (name)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8mb3 DEFAULT NULL COLLATE `utf8mb3_general_ci` COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('DROP INDEX UNIQ_C79A60FF5E237E06 ON product_family');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240220094817 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire ADD conventionnement NUMERIC(10, 2) NOT NULL, CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire DROP conventionnement, CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240220101222 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE prestataire_product_family (id INT AUTO_INCREMENT NOT NULL, prestataire_id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', product_family_id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', products LONGTEXT NOT NULL, INDEX IDX_9B0F7A58BE3DB2B7 (prestataire_id), INDEX IDX_9B0F7A58ADFEE0E7 (product_family_id), UNIQUE INDEX prestataireproductfamily (prestataire_id, product_family_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE prestataire_product_family ADD CONSTRAINT FK_9B0F7A58BE3DB2B7 FOREIGN KEY (prestataire_id) REFERENCES prestataire (id)');
$this->addSql('ALTER TABLE prestataire_product_family ADD CONSTRAINT FK_9B0F7A58ADFEE0E7 FOREIGN KEY (product_family_id) REFERENCES product_family (id)');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE prestataire_product_family');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8mb3 DEFAULT NULL COLLATE `utf8mb3_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240221094604 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\', CHANGE conventionnement conventionnement NUMERIC(10, 2) DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\', CHANGE conventionnement conventionnement NUMERIC(10, 2) NOT NULL');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240227100023 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE selfevalprestaquiz ADD is_submitted TINYINT(1) DEFAULT \'0\' NOT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE selfevalprestaquiz DROP is_submitted');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240302095543 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE user ADD before_first_login_with_presta_enabled TINYINT(1) DEFAULT \'1\' NOT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
$this->addSql('ALTER TABLE user DROP before_first_login_with_presta_enabled');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240306164256 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE dependent_child (id INT AUTO_INCREMENT NOT NULL, adherent_id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', older_than_fourteen TINYINT(1) NOT NULL, conventionnement NUMERIC(10, 2) DEFAULT NULL, INDEX IDX_15BF27C725F06C53 (adherent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE dependent_child ADD CONSTRAINT FK_15BF27C725F06C53 FOREIGN KEY (adherent_id) REFERENCES adherent (id)');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE dependent_child');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240307162630 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE dependent_child CHANGE conventionnement sharedcustodypercentage NUMERIC(10, 2) DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE dependent_child CHANGE sharedcustodypercentage conventionnement NUMERIC(10, 2) DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240311154311 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE subterritory (id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_7D1B925F5E237E06 (name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql("INSERT INTO subterritory (id, name) VALUES (UUID(), 'Bordeaux Nord / Bordeaux La Benauge')");
$this->addSql("INSERT INTO subterritory (id, name) VALUES (UUID(), 'Pays foyen')");
$this->addSql("INSERT INTO subterritory (id, name) VALUES (UUID(), 'Bègles')");
$this->addSql("INSERT INTO subterritory (id, name) VALUES (UUID(), 'Sud Gironde / Bazadais')");
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE subterritory');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240311155641 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE geoloc ADD subterritory_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE geoloc ADD CONSTRAINT FK_E1B7F4E7C8B38DB4 FOREIGN KEY (subterritory_id) REFERENCES subterritory (id)');
$this->addSql('CREATE INDEX IDX_E1B7F4E7C8B38DB4 ON geoloc (subterritory_id)');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE geoloc DROP FOREIGN KEY FK_E1B7F4E7C8B38DB4');
$this->addSql('DROP INDEX IDX_E1B7F4E7C8B38DB4 ON geoloc');
$this->addSql('ALTER TABLE geoloc DROP subterritory_id');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240311161651 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE geoloc ADD quartier VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE geoloc DROP quartier');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240311163934 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent ADD household_composition VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent DROP household_composition');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240311170014 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent ADD household_adult_count INT DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent DROP household_adult_count');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240312135007 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE flux ADD cancellerflux_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE flux ADD CONSTRAINT FK_7252313ABB04641D FOREIGN KEY (cancellerflux_id) REFERENCES flux (id)');
$this->addSql('CREATE INDEX IDX_7252313ABB04641D ON flux (cancellerflux_id)');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE flux DROP FOREIGN KEY FK_7252313ABB04641D');
$this->addSql('DROP INDEX IDX_7252313ABB04641D ON flux');
$this->addSql('ALTER TABLE flux DROP cancellerflux_id');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240313125437 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent ADD cotisation_amount DOUBLE PRECISION DEFAULT NULL, ADD allocation_amount DOUBLE PRECISION DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent DROP cotisation_amount, DROP allocation_amount');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240320144059 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire ADD bic VARCHAR(8) DEFAULT NULL, CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire DROP bic, CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240320153828 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\', CHANGE bic bic VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\', CHANGE bic bic VARCHAR(8) CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci`');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240321101500 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql("INSERT INTO global_parameter (id, name, description, value, mandatory) VALUES (UUID(), 'VIREMENT_RECONVERSION_RAISON_GESTIONNAIRE', 'Raison du gestionnaire pour les virements de reconversion automatisés', null, '1')");
$this->addSql("INSERT INTO global_parameter (id, name, description, value, mandatory) VALUES (UUID(), 'VIREMENT_RECONVERSION_BIC_GESTIONNAIRE', 'Bic du gestionnaire pour les virements de reconversion automatisés', null, '1')");
$this->addSql("INSERT INTO global_parameter (id, name, description, value, mandatory) VALUES (UUID(), 'VIREMENT_RECONVERSION_IBAN_GESTIONNAIRE', 'Iban du gestionnaire pour les virements de reconversion automatisés', null, '1')");
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240321105448 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment ADD is_recurrent TINYINT(1) DEFAULT \'0\' NOT NULL, ADD recurrence_months_count INT DEFAULT NULL, ADD recurrence_month_day INT DEFAULT NULL, ADD recurrence_amount INT DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment DROP is_recurrent, DROP recurrence_months_count, DROP recurrence_month_day, DROP recurrence_amount');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240325100723 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment CHANGE is_recurrent is_recurrent TINYINT(1) DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment CHANGE is_recurrent is_recurrent TINYINT(1) DEFAULT \'0\' NOT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8mb3 DEFAULT NULL COLLATE `utf8mb3_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240403114932 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('UPDATE usergroup SET roles = \'a:16:{i:0;s:14:"ROLE_TRESORIER";i:1;s:30:"ROLE_ADMIN_COMPTOIR_GERER_LIST";i:2;s:30:"ROLE_ADMIN_COMPTOIR_GERER_VIEW";i:3;s:30:"ROLE_ADMIN_ALL_COTISATIONS_ALL";i:4;s:32:"ROLE_ADMIN_ALL_ACHATSMONNAIE_ALL";i:5;s:33:"ROLE_ADMIN_RECONVERSION_GERER_ALL";i:6;s:30:"ROLE_ADMIN_TRANSFERT_GERER_ALL";i:7;s:40:"ROLE_ADMIN_ALL_DEMANDE_ACHATSMONNAIE_ALL";i:8;s:42:"ROLE_ADMIN_OPERATION_PRESTATAIRE_GERER_ALL";i:9;s:39:"ROLE_ADMIN_OPERATION_ADHERENT_GERER_ALL";i:10;s:39:"ROLE_ADMIN_OPERATION_COMPTOIR_GERER_ALL";i:11;s:37:"ROLE_ADMIN_OPERATION_GROUPE_GERER_ALL";i:12;s:36:"ROLE_ADMIN_OPERATION_SIEGE_GERER_ALL";i:13;s:24:"ROLE_ADMIN_HELLOASSO_ALL";i:14;s:19:"ROLE_ADMIN_DONS_ALL";i:15;s:30:"ROLE_ADMIN_ADHERENT_GERER_LIST";}\' WHERE name = "Trésorier"');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('UPDATE usergroup SET roles = \'a:15:{i:0;s:14:"ROLE_TRESORIER";i:1;s:30:"ROLE_ADMIN_COMPTOIR_GERER_LIST";i:2;s:30:"ROLE_ADMIN_COMPTOIR_GERER_VIEW";i:3;s:30:"ROLE_ADMIN_ALL_COTISATIONS_ALL";i:4;s:32:"ROLE_ADMIN_ALL_ACHATSMONNAIE_ALL";i:5;s:33:"ROLE_ADMIN_RECONVERSION_GERER_ALL";i:6;s:30:"ROLE_ADMIN_TRANSFERT_GERER_ALL";i:7;s:40:"ROLE_ADMIN_ALL_DEMANDE_ACHATSMONNAIE_ALL";i:8;s:42:"ROLE_ADMIN_OPERATION_PRESTATAIRE_GERER_ALL";i:9;s:39:"ROLE_ADMIN_OPERATION_ADHERENT_GERER_ALL";i:10;s:39:"ROLE_ADMIN_OPERATION_COMPTOIR_GERER_ALL";i:11;s:37:"ROLE_ADMIN_OPERATION_GROUPE_GERER_ALL";i:12;s:36:"ROLE_ADMIN_OPERATION_SIEGE_GERER_ALL";i:13;s:24:"ROLE_ADMIN_HELLOASSO_ALL";i:14;s:19:"ROLE_ADMIN_DONS_ALL";}\' WHERE name = "Trésorier"');
}
}
<?php
namespace App\Repository;
use App\Entity\PrestataireProductFamily;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<PrestataireProductFamily>
*
* @method PrestataireProductFamily|null find($id, $lockMode = null, $lockVersion = null)
* @method PrestataireProductFamily|null findOneBy(array $criteria, array $orderBy = null)
* @method PrestataireProductFamily[] findAll()
* @method PrestataireProductFamily[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PrestataireProductFamilyRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PrestataireProductFamily::class);
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function add(PrestataireProductFamily $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function remove(PrestataireProductFamily $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}
// /**
// * @return PrestataireProductFamily[] Returns an array of PrestataireProductFamily objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->orderBy('p.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?PrestataireProductFamily
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
<?php
namespace App\Repository;
use App\Entity\ProductFamily;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<ProductFamily>
*
* @method ProductFamily|null find($id, $lockMode = null, $lockVersion = null)
* @method ProductFamily|null findOneBy(array $criteria, array $orderBy = null)
* @method ProductFamily[] findAll()
* @method ProductFamily[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ProductFamilyRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ProductFamily::class);
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function add(ProductFamily $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function remove(ProductFamily $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}
// /**
// * @return ProductFamily[] Returns an array of ProductFamily objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->orderBy('p.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?ProductFamily
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
......@@ -206,6 +206,23 @@ class AppExtension extends AbstractExtension
public function showModalGroupChoice()
{
if (null != $this->security->getUser()) {
/* Enable display of modal after the first login of a user,
* the first time there is an enable prestataire associated to the account.
*
* The user now realizes that a new role is available and can be picked up.
*
* Otherwise, in case prestataire is enabled after first login, user would login automaticaly as
* adherent again without being noticed that prestataire has been enabled.
*/
if($this->container->getParameter('presta_self_init_and_eval')) {
$hasPrestataireEnabled = $this->security->getUser()->getPrestataires() && !$this->security->getUser()->getPrestataires()->isEmpty();
$nowIsfirstLoginWithPrestaEnabled = $this->security->getUser()->getBeforeFirstLoginWithPrestaEnabled();
if ($hasPrestataireEnabled && $nowIsfirstLoginWithPrestaEnabled) {
$this->security->getUser()->setBeforeFirstLoginWithPrestaEnabled(false);
$this->em->flush();
return true;
}
}
if (count($this->security->getUser()->getPossiblegroups()) > 1 && 0 == count($this->security->getUser()->getGroups())) {
return true;
}
......
......@@ -55,6 +55,7 @@ class FormExtension extends AbstractExtension
new \Twig_SimpleFunction('getSetPaymentCodeForm', [$this, 'getSetPaymentCodeForm']),
new \Twig_SimpleFunction('getComptoirEncaisserCotisationForm', [$this, 'getComptoirEncaisserCotisationForm']),
new \Twig_SimpleFunction('getPayerCotisationTAVForm', [$this, 'getPayerCotisationTAVForm']),
new \Twig_SimpleFunction('getPaiementRecurrentCotisationTAVForm', [$this, 'getPaiementRecurrentCotisationTAVForm']),
new \Twig_SimpleFunction('getComptoirEncaisserDonAdherentForm', [$this, 'getComptoirEncaisserDonAdherentForm'])
];
}
......@@ -219,6 +220,11 @@ class FormExtension extends AbstractExtension
return $this->container->get('app.formfactory')->getPayerCotisationTAVForm($user);
}
public function getPaiementRecurrentCotisationTAVForm(User $user)
{
return $this->container->get('app.formfactory')->getPaiementRecurrentCotisationTAVForm($user);
}
public function getComptoirEncaisserDonAdherentForm(User $user)
{
return $this->container->get('app.formfactory')->getComptoirEncaisserDonAdherentForm($user);
......
<?php
namespace App\Utils;
use App\Entity\AchatMonnaieAdherent;
use App\Entity\AchatMonnaiePrestataire;
use App\Entity\Adherent;
use App\Entity\CotisationAdherent;
use App\Entity\CotisationPrestataire;
use App\Entity\Don;
use App\Entity\Geoloc;
use App\Entity\Groupe;
use App\Entity\Payment;
use App\Entity\Prestataire;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Usergroup;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use Payum\Core\Request\GetHumanStatus;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Serializer\SerializerInterface;
class PaymentUtils
{
private $em;
private $serializer;
private $userManager;
private $operationUtils;
private $tavCotisationsUtils;
private $container;
/**
* PaymentUtils constructor.
*/
public function __construct(
EntityManagerInterface $em,
SerializerInterface $serializer,
UserManagerInterface $userManager,
OperationUtils $operationUtils,
TAVCotisationUtils $tavCotisationsUtils,
ContainerInterface $container
) {
$this->em = $em;
$this->serializer = $serializer;
$this->userManager = $userManager;
$this->operationUtils = $operationUtils;
$this->tavCotisationsUtils = $tavCotisationsUtils;
$this->container = $container;
}
/**
* @param $payment
* @return void
* @throws \Exception
*/
public function handlePayzenNotificationCore($payment): void
{
$current_payment_status = $payment->getStatus();
// Payment can be captured if it hasn't been captured before
// Allow STATUS_CAPTURED and STATUS_AUTHORIZED for recurrent payment only.
$flux_array = json_decode($payment->getFluxData(), true);
$type = $payment->getDescription();
if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
AchatMonnaieAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Adherent::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setReconverti(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($dest);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
AchatMonnaiePrestataire::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setReconverti(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_PRESTATAIRE);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($dest);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_COTISATION_ADHERENT == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
CotisationAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Adherent::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setRecu(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($exp);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_COTISATION_PRESTA == $type) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
CotisationPrestataire::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Prestataire::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setRecu(true);
if (null != $flux->getDon()) {
$flux->getDon()->setType(Don::TYPE_DON_PRESTATAIRE);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($exp);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
}
} else if (Payment::TYPE_ADHESION == $type) {
$new_adherent_data = json_decode($payment->getExtraData());
$adherent = new Adherent();
$user = $this->userManager->createUser();
$usergroup = $this->em->getRepository(Usergroup::class)->findOneByName('Adherent');
$group = $this->em->getRepository(Groupe::class)->findOneBy(array('id' => $new_adherent_data->groupe->id));
$user->setEmail($new_adherent_data->user->email);
$user->setUsername($new_adherent_data->user->username);
$user->setFirstname($new_adherent_data->user->firstname);
$user->setLastname($new_adherent_data->user->lastname);
$user->setPlainPassword($new_adherent_data->user->plainPassword);
$user->setEnabled(true);
$user->addPossiblegroup($usergroup);
$user->addGroup($usergroup);
$user->addRole('ROLE_ADHERENT');
$user->setAdherent($adherent);
$adherent->setEcompte('0');
$adherent->setUser($user);
$adherent->setGroupe($group);
if ($adherent->getGeoloc() == null) {
$geoloc = new Geoloc();
$geoloc->setAdresse($new_adherent_data->geoloc->adresse);
$geoloc->setCpostal($new_adherent_data->geoloc->cpostal);
$geoloc->setVille($new_adherent_data->geoloc->ville);
$adherent->setGeoloc($geoloc);
}
$this->em->persist($adherent);
$this->em->flush();
// Create first cotisation
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
CotisationAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$flux->setOperateur($user);
$flux->setExpediteur($adherent);
$flux->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(array('mlc' => true)));
$flux->setRole('Adherent');
$flux->setRecu(true);
// Update payment with new user id, remove user data
$payment->setClientId($user->getId());
$payment->setExtraData('');
$this->em->persist($payment);
} else if (Payment::TYPE_PAIEMENT_COTISATION_TAV == $type || Payment::TYPE_PAIEMENT_RECURRENT_COTISATION_TAV) {
$flux = $this->serializer->deserialize(
$payment->getFluxData(),
AchatMonnaieAdherent::class,
'json',
['disable_type_enforcement' => true]
);
$exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
$flux->setExpediteur($exp);
$dest = $this->em->getRepository(Adherent::class)->find($flux_array['destinataire']);
$flux->setDestinataire($dest);
$op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
$flux->setOperateur($op);
$flux->setReconverti(true);
//For recurrent payment: save don only for first payment, otherwise remove don
//Use current payment status to decide (if first payment -> add don, if not -> remove don)
if (null != $flux->getDon() && $current_payment_status === GetHumanStatus::STATUS_NEW) {
$flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
$flux->getDon()->setOperateur($op);
$flux->getDon()->setExpediteur($dest);
$flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
} else {
$flux->setDon(null);
}
} else {
// Bad request
}
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
if (Payment::TYPE_PAIEMENT_COTISATION_TAV == $type || Payment::TYPE_PAIEMENT_RECURRENT_COTISATION_TAV) {
// Create new flux for cotisation, depending on process
if ($this->container->getParameter('household_based_allowance')) {
$this->tavCotisationsUtils->applyHouseholdAllowance($flux);
} else {
$this->tavCotisationsUtils->applyTauxCotisation($flux);
}
}
}
}
......@@ -3,12 +3,17 @@
namespace App\Utils;
use App\Entity\Adherent;
use App\Entity\CotisationTavPrelevementCorrectionSolde;
use App\Entity\CotisationTavPrelevementDepassementPlafond;
use App\Entity\CotisationTavReversementCorrectionSolde;
use App\Entity\Payment;
use App\Entity\Siege;
use App\Entity\Flux;
use App\Entity\TauxCotisationReversement;
use App\Entity\TauxCotisationPrelevement;
use App\Entity\CotisationTavReversement;
use App\Entity\CotisationTavPrelevement;
use App\Enum\MoyenEnum;
use App\Utils\CustomEntityManager;
use Payum\Core\Request\GetHumanStatus;
use Symfony\Component\Security\Core\Security;
class TAVCotisationUtils
......@@ -44,7 +49,39 @@ class TAVCotisationUtils
return count($existing) > 0;
}
public function checkExistingRecurringPayment(Flux $flux)
{
$recurringPayments = $this->em->getRepository(Payment::class)->findBy([
'isRecurrent' => true,
'clientEmail' => $flux->getDestinataire()->getUser()->getEmail(),
]);
$res = "";
foreach($recurringPayments as $p) {
if (
$p->getStatus() !== GetHumanStatus::STATUS_FAILED
&& $p->getStatus() !== GetHumanStatus::STATUS_CANCELED
&& $p->getStatus() !== GetHumanStatus::STATUS_EXPIRED
&& $p->getDetails()
&& array_key_exists('vads_identifier',$p->getDetails()) //some payment without vads_identifier have status NEW but are not real recurring payments
) {
//Everytime payzen sends a recurring payment notification, notification is
//caught by notifyRecurringPaymentAction, which does not update payment status.
//This is why we can not rely on $p->getStatus to decide if a recurring
//payment is still active or ended or expired.
$reason = "";
if($p->isRecurringPaymentEndedOrExpired($reason) !== true) {
$res .= ($reason . " ");
}
}
}
return $res;
}
/**
* First method to calculate allowance:
* according to a contribution rate defined in user's profile (ProfilDeCotisation).
*
* Apply the cotisation profile rate to the amount paid
* and register the complement as a new flux (only if rate != 1)
*
......@@ -72,14 +109,14 @@ class TAVCotisationUtils
if ($amountDiff > 0) {
// User should receive more than he•she paid: send a new flux to the user to complete its cotisation
$fluxCotis = new TauxCotisationReversement();
$fluxCotis = new CotisationTavReversement();
$fluxCotis->setExpediteur($siege);
$fluxCotis->setDestinataire($flux->getDestinataire());
$fluxCotis->setMontant($amountDiff);
$fluxCotis->setReference("Reversement cotisation après paiement de " . $cotisationAmount . "€ et application du taux " . $cotisationTaux);
} else {
// User should receive less than he•she paid: fetch the difference from his account
$fluxCotis = new TauxCotisationPrelevement();
$fluxCotis = new CotisationTavPrelevement();
$fluxCotis->setExpediteur($flux->getDestinataire());
$fluxCotis->setDestinataire($siege);
$fluxCotis->setMontant(-$amountDiff);
......@@ -95,6 +132,163 @@ class TAVCotisationUtils
}
/**
* Second method to calculate allowance:
* allowance based on user's household.
*
* Rules are as follow:
* - 150 emlc for the first person in user's household
* - 75 emlc for each other adult
* - 75 emlc amount for each dependant child, with a percentage applied if the child is in shared custody:
* 25%, 50% or 75% depending on the shared custody arrangement
*
* Once the full amount is calculated, cap user's balance.
* User account balance is capped at twice the amount previously calculated.
*
* @param Adherent $adherent (by ref)
*/
public function calculateAllowanceAccordingToHousehold(&$adherent) {
// TODO base amounts to param in .env, or in global params ?
// base allowance, for one adult
$mlcAllowanceAmount = 150;
$adultsCount = $adherent->getHouseholdAdultCount();
if ($adultsCount == null) {
return;
}
// increment for each other adult in the household
$mlcAllowanceAmount += 75 * ($adultsCount - 1);
// increment allowance for each dependant child, depending on the shared custody arrangement
$dependentChildren = $adherent->getDependentChildren();
foreach ($dependentChildren as $child) {
$childAllowanceAmount = 75;
$sharedCustodyPercentage = $child->getSharedCustodyPercentage();
if ($sharedCustodyPercentage != null) {
$childAllowanceAmount = $childAllowanceAmount * $sharedCustodyPercentage;
}
$mlcAllowanceAmount += $childAllowanceAmount;
}
$adherent->setAllocationAmount($mlcAllowanceAmount);
}
/**
* Method called to create Flux based on allowance amount (for household based allowance).
* Only create flux if amount paid != allowance amount.
*/
public function applyHouseholdAllowance(Flux $flux) {
// get allowance
$adherent = $flux->getDestinataire();
$cotisationAmount = $flux->getMontant();
// get the mlc amount the user is supposed to receive
$mlcAllowanceAmount = $adherent->getAllocationAmount();
// get the difference between what the user paid and what he•she's supposed to receive
$amountDiff = $mlcAllowanceAmount - $cotisationAmount;
// only create new flux if there is a difference
if ($amountDiff != 0) {
if ($flux->getExpediteur() instanceof Siege) {
$siege = $flux->getExpediteur();
} else {
$siege = $flux->getExpediteur()->getGroupe()->getSiege();
}
if ($amountDiff > 0) {
// User should receive more than he•she paid: send a new flux to the user to complete its cotisation
$fluxCotis = new CotisationTavReversement();
$fluxCotis->setExpediteur($siege);
$fluxCotis->setDestinataire($adherent);
$fluxCotis->setMontant($amountDiff);
$fluxCotis->setReference("Versement de l'allocation complémentaire après paiement de " . $cotisationAmount . "€ pour atteindre une allocation de " . $mlcAllowanceAmount . " MonA.");
} else {
// User should receive less than he•she paid: fetch the difference from his account
$fluxCotis = new CotisationTavPrelevement();
$fluxCotis->setExpediteur($adherent);
$fluxCotis->setDestinataire($siege);
$fluxCotis->setMontant(-$amountDiff);
$fluxCotis->setReference("Réduction de l'allocation correspondant à un paiement de " . $cotisationAmount . "€ pour atteindre une allocation de " . $mlcAllowanceAmount . " MonA.");
}
$fluxCotis->setOperateur($flux->getOperateur());
$fluxCotis->setRole($flux->getRole());
$fluxCotis->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($fluxCotis);
$this->operationUtils->executeOperations($fluxCotis);
}
}
/**
* Method called to create Flux based on allowance amount (for household based allowance).
*/
public function withdrawDownToTheCeiling(Adherent $adherent)
{
$balance = $adherent->getEmlcAccount()->getBalance();
$ceiling = $adherent->getCeiling();
$siege = $this->em->getRepository(Siege::class)->getTheOne();
// get the amount we want to withdraw
$amountDiff = $ceiling - $balance;
if ($amountDiff >= 0) {
throw new \Exception("Impossible de prélèver : le solde de l'adhérent est inférieur ou égal au plafond.");
}
$flux = new CotisationTavPrelevementDepassementPlafond();
$flux->setExpediteur($adherent);
$flux->setDestinataire($siege);
$flux->setMontant(-$amountDiff);
$flux->setReference("Prélèvement pour ramener le solde de " . $balance . " MonA sous le plafond de " . $ceiling . " MonA.");
$flux->setOperateur($this->security->getUser());
$flux->setRole($this->security->getUser()->getGroups()[0]->__toString());
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
return $amountDiff;
}
/**
* Method called to create Flux to fix balance (for household based allowance).
*/
public function fixBalance(Adherent $adherent, $fixedBalance, $justification)
{
$balance = $adherent->getEmlcAccount()->getBalance();
$siege = $this->em->getRepository(Siege::class)->getTheOne();
$amountDiff = $fixedBalance - $balance;
if ($amountDiff >= 0) {
//Accroissement du solde
$flux = new CotisationTavReversementCorrectionSolde();
$flux->setExpediteur($siege);
$flux->setDestinataire($adherent);
$flux->setReference(
"Reversement pour corriger le solde de " . $balance . " MonA à " . $fixedBalance . " MonA : " . $justification
);
} else {
//Réduction du solde
$flux = new CotisationTavPrelevementCorrectionSolde();
$flux->setExpediteur($adherent);
$flux->setDestinataire($siege);
$flux->setReference(
"Prélèvement pour corriger le solde de " . $balance . " MonA à " . $fixedBalance . " MonA : " . $justification
);
}
$flux->setMontant(abs($amountDiff));
$flux->setOperateur($this->security->getUser());
$flux->setRole($this->security->getUser()->getGroups()[0]->__toString());
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
}
/**
* Get the last cotisation of an adhérent
*
* @param Adherent $adherent
......
......@@ -8,7 +8,7 @@
{% include '@kohinos/presta/block/infos.html.twig' %}
{% endif %}
{# {% include '@kohinos/block/transactions.html.twig' %} #}
{% include '@kohinos/block/operations.html.twig' %}
{% include '@kohinos/block/operations.html.twig' with {'display_cancel_transaction_btn' : tav_env} %}
{% if not tav_env %}
{% if getCurrentPrestataire().mlc == true %}
{% include '@kohinos/block/operations.html.twig' with {'title' : 'Compte de fonctionnement €', 'operations': getLastOperations(app.request, app.user, constant('App\\Enum\\CurrencyEnum::CURRENCY_EURO'))} %}
......@@ -29,6 +29,6 @@
{% include '@kohinos/presta/block/transaction_presta.html.twig' %}
{% include '@kohinos/presta/block/transaction_adherent.html.twig' %}
{% endif %}
{% if getCurrentPrestataire().mlc == false and getCurrentPrestataire().solidoume == false and getCurrentPrestataire().emlcAccount.balance > 0 %}
{% if getCurrentPrestataire().mlc == false and getCurrentPrestataire().solidoume == false and getCurrentPrestataire().emlcAccount.balance > 0 and automatisation_reconversion == 0 %}
{% include '@kohinos/presta/block/reconversion.html.twig' %}
{% endif %}
\ No newline at end of file
......@@ -22,7 +22,7 @@
<div class="modal-body">
{{ modal_content }}
</div>
{% if btn_primary is not empty and btn_secondary is not empty %}
{% if btn_primary is not empty or btn_secondary is not empty %}
<div class="modal-footer">
{% if btn_primary is not empty %}
<button type="button" class="btn btn-primary">{{ btn_primary }}</button>
......
......@@ -17,6 +17,10 @@
<th scope="col">Type</th>
<th scope="col">Operation</th>
<th scope="col">Montant</th>
{# Display column to store btn to cancel transaction adherent prestataire #}
{% if display_cancel_transaction_btn is defined and display_cancel_transaction_btn %}
<th scope="col"></th>
{% endif %}
</tr>
</thead>
<tbody>
......@@ -55,7 +59,28 @@
Moyen : {{ operation.flux.moyen|trans }}
</div>
</td>
<td class='text-right'><span class='font-weight-bold {{ (operation.montant < 0)? '':'text-primary' }}'>{{ (operation.montant < 0)? '- ':'+ ' }} {{ operation.montant|abs|number_format(2) }} {{ getCurrencyName(operation.currency) }}</span></td>
<td><span class='font-weight-bold {{ (operation.montant < 0)? '':'text-primary' }}'>{{ (operation.montant < 0)? '- ':'+ ' }} {{ operation.montant|abs|number_format(2) }} {{ getCurrencyName(operation.currency) }}</span></td>
{# Display btn to cancel transaction adherent prestataire #}
{% if display_cancel_transaction_btn is defined and display_cancel_transaction_btn
and operation.flux.type is same as(constant('App\\Entity\\Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE'))
and app.user in operation.flux.destinataire.users %}
<td class='text-right'>
{# If transaction has been cancelled already #}
{% if operation.flux.cancellerFlux %}
<button class="btn btn-sm" title="Cette transaction a déjà été annulée." disabled>
<i class="fas fa-undo"></i>
</button>
{% else %}
<a href="{{ path('cancel_transaction_adherent_prestataire', {'id': operation.flux.id}) }}"
type="button"
class="btn btn-sm btn-primary"
title="Annuler cette transaction."
onclick="return confirm('Êtes-vous sûr de vouloir annuler cette transaction et rembourser le client ? L\'opération est irréversible.')">
<i class="fas fa-undo"></i>
</a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
......
......@@ -33,6 +33,12 @@
</li>
{% endfor %}
</ul>
{% if tav_env and automatisation_reconversion and admin.isReconversionAdmin is defined and admin.isReconversionAdmin %}
<a href="{{ path('credit_transfer_file') }}" type="button" class="btn btn-default">
<i class="fa fa-share-square-o" aria-hidden="true"></i>
Générer SEPA
</a>
{% endif %}
</div>
</div>
{% if admin.total is defined and admin.total > 0 %}
......
{% extends '@kohinos/block/onetransaction.html.twig' %}
{% block blocktitle %}
<i class="fa fa-exchange fa-exchange-alt mr-4"></i> {% if tav_env %}{{ 'Encaisser la cotisation d\'un habitant'|trans }}{% else %}{{ 'Vente de monnaie numérique à un adhérent'|trans }}{% endif %}
<i class="fa fa-exchange fa-exchange-alt mr-4"></i> {% if tav_env %}{{ 'Encaisser la COTISATION d\'un habitant'|trans }}{% else %}{{ 'Vente de monnaie numérique à un adhérent'|trans }}{% endif %}
{% endblock blocktitle %}
{% block blockcontent %}
{% if app.session.has('_comptoirgere') %}
......
......@@ -10,6 +10,12 @@
<h3>
{% if flux.type == 'reconversion' %}
{{ 'Demande de reconversion' }}
{% elseif flux.type == 'prelevement_cotisation_adherent_depassement_plafond' %}
{{ "Réduction de l'allocation après dépassement du plafond" }}
{% elseif flux.type == 'prelevement_cotisation_adherent_correction_solde' %}
{{ "Correction de solde (réduction de l'allocation) suite à une erreur de cotisation" }}
{% elseif flux.type == 'reversement_cotisation_adherent_correction_solde' %}
{{ "Correction de solde (allocation complémentaire de la caisse) suite à une erreur de cotisation" }}
{% else %}
{{ flux.parenttype|capitalize }} : {{ flux.type|replace({'_' : ' => '}) }}
{% endif %}
......
......@@ -5,8 +5,35 @@
{% endblock blocktitle %}
{% block blocksubtitle %}
{% endblock blocksubtitle %}
{% block blockcontent %}
{% set form = getPrestataireInfosForm(app.user) %}
{# Use macro as a template : macro for a prestataire product family entry in the form collection #}
{% import _self as formMacros %}
{% macro printPrestataireProductFamilyForm(prestataireProductFamilyForm, formName = "") %}
<div class="presta-products-family">
<div class="presta-products-family-fields">
<div class="form-group presta-products-family-form-group">
{{ form_label(prestataireProductFamilyForm.productFamily) }}
{{ form_widget(prestataireProductFamilyForm.productFamily) }}
</div>
<div class="form-group presta-products-family-form-group">
{{ form_label(prestataireProductFamilyForm.products) }}
{{ form_widget(prestataireProductFamilyForm.products) }}
</div>
<div class="presta-products-family-duplicate-warning">{{'Cette famille de produits est déjà renseignée.'|trans}}</div>
</div>
<a href="#" class="presta-products-family-delete">
<i
class="fa fa-times delete-icon"
id="formPrestataireInfos_prestataireProductFamilies_{{ formName == '' ? '__name__' : formName }}_delete"
></i>
</a>
</div>
{% endmacro %}
{{form_start(form)}}
{{ form_row(form.raison) }}
{{ form_row(form.statut) }}
......@@ -19,11 +46,15 @@
</div>
{% endif %}
{{ form_row(form.siret) }}
{{ form_row(form.bic) }}
{{ form_row(form.iban) }}
{{ form_row(form.responsable) }}
{{ form_row(form.metier) }}
{{ form_row(form.horaires) }}
{{ form_row(form.web) }}
{% if form.reconversionFrequency is defined %}
{{ form_row(form.reconversionFrequency) }}
{% endif %}
<hr/>
{{ form_row(form.media) }}
{{ form_row(form.description) }}
......@@ -39,6 +70,31 @@
<hr/>
{{ form_row(form.newcaissiers) }}
<hr/>
{% if form.prestataireProductFamilies is defined %}
<h4>{{ form_label(form.prestataireProductFamilies) }}</h4>
{{ form_errors(form.prestataireProductFamilies) }}
<div
class="presta-products-families-list"
data-index="{{ form.prestataireProductFamilies|length > 0 ? form.prestataireProductFamilies|last.vars.name + 1 : 0 }}"
data-prototype="{{ formMacros.printPrestataireProductFamilyForm(form.prestataireProductFamilies.vars.prototype)|e('html_attr') }}"
>
{% for prestataireProductFamilyForm in form.prestataireProductFamilies %}
{{ formMacros.printPrestataireProductFamilyForm(prestataireProductFamilyForm, prestataireProductFamilyForm.vars.name) }}
{% endfor %}
{% do form.prestataireProductFamilies.setRendered() %}
</div>
<button
type="button"
id="add-prestataire-products-family"
class="btn-primary btn"
data-collection-holder-class="presta-products-families-list"
>
<i class="fa fa-plus"></i>
</button>
<hr/>
{% endif %}
{{ form_row(form.acceptemlc) }}
{{ form_row(form.save) }}
{{form_end(form)}}
......
......@@ -44,6 +44,9 @@
{% if presta.siret != null %}
<h5 class="card-subtitle mb-3">{{'SIRET'|trans}} : {{ presta.siret }}</h5>
{% endif %}
{% if app.user and presta_extra_data and presta.conventionnement != null and presta.conventionnement > 0 %}
<h5 class="card-subtitle mb-3">{{'Pourcentage de conventionnement'|trans}} : {{ presta.conventionnement * 100 }}%</h5>
{% endif %}
{% if presta.horaires != null %}
<h6 class="card-subtitle text-muted mb-3">{{'Horaires'|trans}} : {{ presta.horaires|raw }}</h6>
{% endif %}
......@@ -77,12 +80,40 @@
{% endfor %}
</ul>
{% endif %}
{% if presta_extra_data and presta.prestataireProductFamilies|length > 0%}
<div class="card-header"><h2>{{ 'Produits vendus'|trans }}</h2></div>
<ul class="list-group list-group-flush">
{% for prestataireProductFamily in presta.prestataireProductFamilies %}
{% if prestataireProductFamily.products|length > 0%}
<li class="list-group-item">
{{ prestataireProductFamily.productFamily.name }}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% if app.user and presta_self_init_and_eval and presta.selfEvalPrestaQuiz and presta.selfEvalPrestaQuiz.isSubmitted %}
{% set prestataire = presta %}
<div class="card-header">
<h2>{{ 'Questionnaire'|trans }}</h2>
{% if app.user in prestataire.users and is_granted("ROLE_PRESTATAIRE") %}
<a class='btn btn-primary float-right' href="{{ path('quiz-presta-edit') }}">Corriger</a>
{% endif %}
</div>
<div class="card-body">
{% if presta.marketChannelFunction == 'distributor' %}
{% include '@kohinos/tav/prestaquiz/distributor_core.html.twig' %}
{% else %}
{% include '@kohinos/tav/prestaquiz/producer_core.html.twig' %}
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% endblock %}
{% block contenat %}
<div class='container newslist mt-5'>
......
{% set balance = object.user.adherent.emlcAccount.balance %}
{% set ceiling = object.user.adherent.ceiling %}
{% if balance and ceiling and balance > ceiling %}
{% set diff = balance - ceiling %}
{% set warnMsg =
'Vous vous apprêtez à prélever ' ~ diff|number_format ~ ' MonA sur le compte de '
~ object.user.adherent ~ ' afin de ramener le solde de son compte au niveau de son plafond autorisé. Poursuivre ?'
%}
<a class="btn btn-sm btn-default"
href="{{ admin.generateObjectUrl('withdrawDownToTheCeiling', object) }}"
onclick="return confirm('{{ warnMsg }}')">
Prélever
</a>
{% endif %}
......@@ -5,17 +5,27 @@
{% endblock blocktitle %}
{% block blockcontent %}
{% set form = getPayerCotisationTAVForm(app.user) %}
{% set formRecurrentPayment = getPaiementRecurrentCotisationTAVForm(app.user) %}
{% if form.montant.vars.value == false %}
{% if form.montant.vars.value == false and not household_based_allowance %}
<p>{{ 'Vous n\'avez pas de profil de cotisation associé, vous ne pouvez donc pas payer de cotisation.'|trans }}</p>
<p>{{ 'Veuillez contacter un administrateur.'|trans }}</p>
<p>{{ 'Veuillez contacter un•e administrateur•rice.'|trans }}</p>
{% elseif form.montant.vars.value == false and household_based_allowance %}
<p>{{ 'Vous n\'avez pas de montant de cotisation renseigné dans votre profil, vous ne pouvez donc pas payer de cotisation.'|trans }}</p>
<p>{{ 'Veuillez contacter un•e administrateur•rice.'|trans }}</p>
{% else %}
<p>
{{ 'Montant de la cotisation à payer'|trans }} : <span class="paiement_cotisation_montant">{{ form.montant.vars.value }}</span>
</p>
<select name="tav-cotisation-payment-type" id="tav-cotisation-payment-type" class="form-control" autocomplete="off">
<option value="">Choisir une option de paiement</option>
<option value="instant-payment">Paiement immédiat</option>
<option value="recurrent-payment">Paiement récurrent</option>
</select>
<div id="tav-cotisation-instant-payment-container" class="tav-cotisation-payment-form">
{{form_start(form)}}
{% if form.don is defined %}
{% include '@kohinos/tav/block/adherent_payer_cotisation_don.html.twig' %}
{% include '@kohinos/tav/block/adherent_payer_cotisation_don.html.twig' with {'parentForm': form} %}
<hr/>
<p><b>{{ 'TOTAL A PAYER'|trans }} : <span class="achat_monnaie_montant_total">{{ form.montant.vars.value }}</span></b></p>
{% endif %}
......@@ -26,6 +36,22 @@
{{ form_widget(form.saveHelloAsso) }}
{% endif %}
{{form_end(form)}}
</div>
<div id="tav-cotisation-recurrent-payment-container" class="tav-cotisation-payment-form">
{{form_start(formRecurrentPayment)}}
{{ form_row(formRecurrentPayment.nombreMois) }}
{{ form_row(formRecurrentPayment.jourPrelevement) }}
{% if formRecurrentPayment.don is defined %}
{% include '@kohinos/tav/block/adherent_payer_cotisation_don.html.twig' with {'parentForm': formRecurrentPayment} %}
<hr/>
<p><b>{{ 'TOTAL PREMIER PAIEMENT'|trans }} : <span class="achat_monnaie_premier_montant_total">{{ formRecurrentPayment.montant.vars.value }}</span></b></p>
<p><b>{{ 'ÉCHÉANCES SUIVANTES'|trans }} : <span class="achat_monnaie_montant_total">{{ formRecurrentPayment.montant.vars.value }}</span></b></p>
{% endif %}
{% if formRecurrentPayment.save is defined %}
{{ form_widget(formRecurrentPayment.save) }}
{% endif %}
{{form_end(formRecurrentPayment)}}
</div>
{% endif %}
......
......@@ -4,5 +4,5 @@
En plus de ma cotisation je souhaite faire un don pour ce mois
{% endblock blocktitle %}
{% block blockcontent %}
{{ form_row(form.don, {row_attr:{style: 'max-width: 200px;margin: 0 auto;'}}) }}
{{ form_row(parentForm.don, {row_attr:{style: 'max-width: 200px;margin: 0 auto;'}}) }}
{% endblock blockcontent %}
\ No newline at end of file
{% extends '@kohinos/block/onetransaction.html.twig' %}
{% block blocktitle %}
<i class="fa fa-exchange fa-exchange-alt mr-4"></i> {{ 'Encaisser la cotisation d\'un habitant'|trans }}
<i class="fa fa-exchange fa-exchange-alt mr-4"></i> {{ 'Encaisser la COTISATION d\'un habitant'|trans }}
{% endblock blocktitle %}
{% block blockcontent %}
{% if app.session.has('_comptoirgere') %}
......@@ -9,9 +9,13 @@
<h5> <b>Montant de la cotisation : <span id="formEncaisserCotisationAdherent-montant-display"></span></b></h5>
<br/>
</div>
<div id="formEncaisserCotisationAdherent-no-profile" style="display:none">
<p class="formEncaisserCotisationAdherent-no-profile">
<div id="formEncaisserCotisationAdherent-no-cotisation-amount" style="display:none">
<p class="formEncaisserCotisationAdherent-no-cotisation-amount">
{% if household_based_allowance %}
L'habitant•e n'a pas de montant de cotisation renseigné, impossible de l'encaisser.
{% else %}
L'habitant•e n'a pas de profil de cotisation affecté, impossible de l'encaisser.
{% endif %}
</p>
</div>
{% set form = getComptoirEncaisserCotisationForm(app.user) %}
......
{% extends '@kohinos/block/onetransaction.html.twig' %}
{% block blocktitle %}
<i class="fa fa-exchange fa-exchange-alt mr-4"></i> {{ 'Encaisser un don d\'un habitant'|trans }}
<i class="fa fa-gift fa-gift-alt mr-4"></i> {{ 'L\'habitant souhaite faire un DON'|trans }}
{% endblock blocktitle %}
{% block blockcontent %}
{% if app.session.has('_comptoirgere') %}
......
......@@ -14,7 +14,7 @@
<b>{{ app.session.get('_prestagere') }}</b>
</div>
<h2 class='text-center w-100 mt-4 mb-4'>{{ "Paiement en MonA"|trans }}</h2>
<h2 class='text-center w-100 mt-4 mb-4'>{{ "Paiement"|trans }}</h2>
{% else %}
<div class="mt-3 payment-page-header">
<a href='{{ path('encaissement') }}'>
......@@ -26,6 +26,25 @@
<h2 class='text-center w-100 mt-4 mb-4 payment-recap'></h2>
<div id="payment-validation-subheader">{{ "Veuillez entrer votre code de validation"|trans }}</div>
{% endif %}
{# SHOW BALANCE MODALE#}
{% if insufficientBalance is not null %}
{% set modal_id = 'showbalancemodal' %}
{% set modal_title = 'Mon solde'|trans %}
{% set modal_content %}
<div>
{{ "Solde restant :"|trans }} {{ insufficientBalance }} {{ KOH_MLC_NAME_SMALL|default('') }}
</div>
{% endset %}
{% include '@kohinos/block/modal.html.twig' with {'btn_primary' : null, 'btn_secondary' : 'Ok'} %}
<div id="display-balance-text-container">
<div>
Votre solde est insuffisant, voulez-vous consulter votre solde ?
</div>
<a class='btn btn-primary' data-toggle="modal" data-target="#{{ modal_id }}" title='{{ 'Mon solde'|trans }}' href="#">{{ 'Oui'|trans }}</a>
<a class='btn btn-primary' id="display-balance-button-no" href="#">{{ 'Non'|trans }}</a>
</div>
{% endif %}
<br/>
<div class='text-center mb-5'>
......@@ -52,10 +71,27 @@
<div style="display:none;">
{{ form_row(form.adherent) }}
{{ form_row(form.montant) }}
{% if form.montantPanier is defined %}
{{ form_row(form.montantPanier) }}
{% endif %}
</div>
{% else %}
{{ form_row(form.adherent) }}
{% if form.montantPanier is defined %}
{{ form_row(form.montantPanier) }}
<div class="presta-conventionnement-applied-text form-text text-muted" style="display:none;">
Conventionnement du point de vente : {{ conventionnement * 100 }}% ;
<span class="encaissment-emlc-max-amount-container">
le montant maximum payable en MonA est de : <span class="encaissment-emlc-max-amount"></span> MonA
</span>
</div>
<div class="montant-panier-error form-text" style="display:none;">La valeur est invalide.</div>
{% endif %}
{{ form_row(form.montant) }}
<br/>
{% endif %}
<br/>
......@@ -65,7 +101,15 @@
</div>
<script type="text/javascript">
var formEncaissementValidation = '{{ validation }}';
var KOH_MLC_NAME_SMALL = "{{ KOH_MLC_NAME_SMALL|default('') }}";
</script>
{% if conventionnement is defined %}
<script type="text/javascript">
var formEncaissementConventionnement = '{{ conventionnement }}';
</script>
{% endif %}
{% endblock %}
{% block footer %}{% endblock footer %}
\ No newline at end of file
{% extends admin.getTemplate('base_list_field') %}
{% block field %}
{%- spaceless %}
{% set balance = object.user.adherent.emlcAccount.balance %}
{% set ceiling = object.user.adherent.ceiling %}
{% if not ceiling %}
{% set class = 'label label-warning' %}
{% set text = 'profil incomplet' %}
{% elseif balance > ceiling %}
{% set class = 'label label-danger' %}
{% set text = balance ~ ' > ' ~ ceiling %}
{% else %}
{% set class = 'label label-success' %}
{% set text = balance ~ " &leq; " ~ ceiling %}
{% endif %}
<span class="{{ class }}">{{ text|raw }}</span>
{% endspaceless -%}
{% endblock %}
......@@ -5,7 +5,9 @@
{% block content %}
<div class='container' style='max-width: 800px;'>
<div class="payment-done-container">
<h1 class="text-center w-100 mt-4 payment-done-title">Paiement réussi !</h1>
<h1 class="text-center w-100 mt-4 payment-done-title">
{{ 'Paiement réussi !'|trans }}
</h1>
<br/>
......@@ -20,6 +22,12 @@
<br/>
{% if remainingAmount is defined %}
<h5>{{ 'Reste à payer en euros :'|trans }} {{ remainingAmount }}</h5>
<br/>
{% endif %}
<a class='btn btn-xs btn-primary mt-4' href='{{ path('encaissement') }}'>
{{ 'Nouvel encaissement'|trans }}
</a>
......
{# View for prestataire questionnaire sections that are common to distributors and producers #}
<h2><i class="fas fa-universal-access"></i> Accessibilité et Inclusivité</h2>
<div class="container">
<div class="row">
{{ form_label(form.accessib_geophy) }}
<div class="col">{{ form_widget(form.accessib_geophy) }}</div>
<div class="col">{{ form_row(form.accessib_geophy_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_tempor) }}
<div class="col">{{ form_widget(form.accessib_tempor) }}</div>
<div class="col">{{ form_row(form.accessib_tempor_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_edupop) }}
<div class="col">{{ form_widget(form.accessib_edupop) }}</div>
<div class="col">{{ form_row(form.accessib_edupop_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_divers) }}
<div class="col">{{ form_widget(form.accessib_divers) }}</div>
<div class="col">{{ form_row(form.accessib_divers_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_vulner) }}
<div class="col">{{ form_widget(form.accessib_vulner) }}</div>
<div class="col">{{ form_row(form.accessib_vulner_comment) }}</div>
</div>
</div>
<div class="container global-evaluation-container">
<div class="row">
{{ form_label(form.accessib_global) }}
<div class="col">{{ form_widget(form.accessib_global) }}</div>
<div class="col">{{ form_row(form.accessib_global_comment) }}</div>
</div>
</div>
{% if form.review_accessib_global is defined %}
<br/>
<div class="container review-container">
<div class="row">
{{ form_label(form.review_accessib_global) }}
<div class="col">{{ form_widget(form.review_accessib_global) }}</div>
<div class="col">{{ form_row(form.review_accessib_global_comment) }}</div>
</div>
</div>
{% endif %}
<br/>
<br/>
<br/>
<h2><i class="fas fa-heart"></i> Bien-être au travail</h2>
<div class="container">
<div class="row">
{{ form_label(form.bienetre_format) }}
<div class="col">{{ form_widget(form.bienetre_format) }}</div>
<div class="col">{{ form_row(form.bienetre_format_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.bienetre_impgou) }}
<div class="col">{{ form_widget(form.bienetre_impgou) }}</div>
<div class="col">{{ form_row(form.bienetre_impgou_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.bienetre_bienet) }}
<div class="col">{{ form_widget(form.bienetre_bienet) }}</div>
<div class="col">{{ form_row(form.bienetre_bienet_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.bienetre_recben) }}
<div class="col">{{ form_widget(form.bienetre_recben) }}</div>
<div class="col">{{ form_row(form.bienetre_recben_comment) }}</div>
</div>
</div>
<div class="container global-evaluation-container">
<div class="row">
{{ form_label(form.bienetre_global) }}
<div class="col">{{ form_widget(form.bienetre_global) }}</div>
<div class="col">{{ form_row(form.bienetre_global_comment) }}</div>
</div>
</div>
{% if form.review_bienetre_global is defined %}
<br/>
<div class="container review-container">
<div class="row">
{{ form_label(form.review_bienetre_global) }}
<div class="col">{{ form_widget(form.review_bienetre_global) }}</div>
<div class="col">{{ form_row(form.review_bienetre_global_comment) }}</div>
</div>
</div>
{% endif %}
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
{# Check if review mode to adapt title #}
{% if form.admin_edit is defined %}
<h1>Revue auto-évaluation distributeur {{ prestataire.raison }}</h1>
{% elseif form.presta_edit_after_definitive_submission is defined %}
<h1>Correction du questionnaire d'auto-évaluation</h1>
{% else %}
<h1>Inscription point de vente (2/2) : auto-évaluation</h1>
{% endif %}
<br/>
<br/>
{% include '@kohinos/tav/prestaquiz/distributor_core.html.twig' %}
{% endblock %}
\ No newline at end of file
{# Form theme use is mandatory to insert non-text stuff (such as html, icons...) in the choice type choices label. #}
{% form_theme form '@kohinos/tav/prestaquiz/form_theme.html.twig' %}
{{ form_start(form) }}
{# includes twig common to distributors and producers #}
{% include '@kohinos/tav/prestaquiz/base.html.twig' %}
<br/>
<br/>
<br/>
<h2><i class="fas fa-coins"></i> Transparence et juste rémunération</h2>
<div class="container">
<div class="row">
{{ form_label(form.transpar_jusrem) }}
<div class="col">{{ form_widget(form.transpar_jusrem) }}</div>
<div class="col">{{ form_row(form.transpar_jusrem_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.transpar_transp) }}
<div class="col">{{ form_widget(form.transpar_transp) }}</div>
<div class="col">{{ form_row(form.transpar_transp_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.transpar_relpro) }}
<div class="col">{{ form_widget(form.transpar_relpro) }}</div>
<div class="col">{{ form_row(form.transpar_relpro_comment) }}</div>
</div>
</div>
<div class="container global-evaluation-container">
<div class="row">
{{ form_label(form.transpar_global) }}
<div class="col">{{ form_widget(form.transpar_global) }}</div>
<div class="col">{{ form_row(form.transpar_global_comment) }}</div>
</div>
</div>
{% if form.review_transpar_global is defined %}
<br/>
<div class="container review-container">
<div class="row">
{{ form_label(form.review_transpar_global) }}
<div class="col">{{ form_widget(form.review_transpar_global) }}</div>
<div class="col">{{ form_row(form.review_transpar_global_comment) }}</div>
</div>
</div>
{% endif %}
<br/>
<br/>
<br/>
<h2><i class="fas fa-seedling"></i> Pratiques agricoles durables</h2>
<div class="container">
<div class="row">
{{ form_label(form.disagdur_labels) }}
<div class="col">{{ form_widget(form.disagdur_labels) }}</div>
<div class="col">{{ form_row(form.disagdur_labels_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.disagdur_condur) }}
<div class="col">{{ form_widget(form.disagdur_condur) }}</div>
<div class="col">{{ form_row(form.disagdur_condur_comment) }}</div>
</div>
</div>
<div class="container global-evaluation-container">
<div class="row">
{{ form_label(form.disagdur_global) }}
<div class="col">{{ form_widget(form.disagdur_global) }}</div>
<div class="col">{{ form_row(form.disagdur_global_comment) }}</div>
</div>
</div>
{% if form.review_disagdur_global is defined %}
<br/>
<div class="container review-container">
<div class="row">
{{ form_label(form.review_disagdur_global) }}
<div class="col">{{ form_widget(form.review_disagdur_global) }}</div>
<div class="col">{{ form_row(form.review_disagdur_global_comment) }}</div>
</div>
</div>
{% endif %}
<br/>
<br/>
<br/>
<h2><i class="fas fa-map-marker-alt"></i> Localité des produits</h2>
<div class="container">
<div class="row">
{{ form_label(form.localite_probru) }}
<div class="col">{{ form_widget(form.localite_probru) }}</div>
<div class="col">{{ form_row(form.localite_probru_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.localite_protra) }}
<div class="col">{{ form_widget(form.localite_protra) }}</div>
<div class="col">{{ form_row(form.localite_protra_comment) }}</div>
</div>
</div>
<div class="container global-evaluation-container">
<div class="row">
{{ form_label(form.localite_global) }}
<div class="col">{{ form_widget(form.localite_global) }}</div>
<div class="col">{{ form_row(form.localite_global_comment) }}</div>
</div>
</div>
{% if form.review_localite_global is defined %}
<br/>
<div class="container review-container">
<div class="row">
{{ form_label(form.review_localite_global) }}
<div class="col">{{ form_widget(form.review_localite_global) }}</div>
<div class="col">{{ form_row(form.review_localite_global_comment) }}</div>
</div>
</div>
{% endif %}
<br/>
<br/>
{% if form.temporary_save is defined %}
<div class="prestaquizz_temporary_save_container">
{{ form_row(form.temporary_save) }}
</div>
{% endif %}
{{ form_end(form) }}
\ No newline at end of file
{# Custom form theme based on file : Resources/views/Form/bootstrap_4_layout.html.twig
where unique change is use of filter |raw on line
{{- label is not same as(false) ? (translation_domain is same as(false) ? label|raw : label|trans(label_translation_parameters, translation_domain))|raw -}}
This is to allow use of html in choice type choices label,
as suggested here : https://stackoverflow.com/questions/45562135/html-in-label-choice-form
#}
{% block checkbox_radio_label -%}
{#- Do not display the label if widget is not defined in order to prevent double label rendering -#}
{%- if widget is defined -%}
{% set is_parent_custom = parent_label_class is defined and ('checkbox-custom' in parent_label_class or 'radio-custom' in parent_label_class or 'switch-custom' in parent_label_class) %}
{% set is_custom = label_attr.class is defined and ('checkbox-custom' in label_attr.class or 'radio-custom' in label_attr.class or 'switch-custom' in label_attr.class) %}
{%- if is_parent_custom or is_custom -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' custom-control-label')|trim}) -%}
{%- else %}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' form-check-label')|trim}) -%}
{%- endif %}
{%- if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif -%}
{%- if required -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) -%}
{%- endif -%}
{%- if label is not same as(false) and label is empty -%}
{%- if label_format is not empty -%}
{%- set label = label_format|replace({
'%name%': name,
'%id%': id,
}) -%}
{%- else -%}
{%- set label = name|humanize -%}
{%- endif -%}
{%- endif -%}
{{ widget|raw }}
<label{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}>
{{- label is not same as(false) ? (translation_domain is same as(false) ? label|raw : label|trans(label_translation_parameters, translation_domain))|raw -}}
{{- form_errors(form) -}}
</label>
{%- endif -%}
{%- endblock checkbox_radio_label %}
\ No newline at end of file
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
<div class='container' style='max-width: 800px;'>
<h1>Inscription point de vente (1/2) : identification</h1>
{{ form_start(form) }}
{{ form_end(form) }}
</div>
{% endblock %}
\ No newline at end of file
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
{# Check if review mode to adapt title #}
{% if form.admin_edit is defined %}
<h1>Revue auto-évaluation producteur {{ prestataire.raison }}</h1>
{% elseif form.presta_edit_after_definitive_submission is defined %}
<h1>Correction du questionnaire d'auto-évaluation</h1>
{% else %}
<h1>Inscription point de vente (2/2) : auto-évaluation</h1>
{% endif %}
<br/>
<br/>
{% include '@kohinos/tav/prestaquiz/producer_core.html.twig' %}
{% endblock %}
\ No newline at end of file
{# Form theme use is mandatory to insert non-text stuff (such as html, icons...) in the choice type choices label. #}
{% form_theme form '@kohinos/tav/prestaquiz/form_theme.html.twig' %}
{{ form_start(form) }}
{# includes twig common to distributors and producers #}
{% include '@kohinos/tav/prestaquiz/base.html.twig' %}
<br/>
<br/>
<br/>
<h2><i class="fas fa-seedling"></i> Pratiques agricoles durables</h2>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_labels) }}
<div class="col">{{ form_widget(form.proagdur_labels) }}</div>
<div class="col">{{ form_row(form.proagdur_labels_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_valbio) }}
<div class="col">{{ form_widget(form.proagdur_valbio) }}</div>
<div class="col">{{ form_row(form.proagdur_valbio_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_ecorec) }}
<div class="col">{{ form_widget(form.proagdur_ecorec) }}</div>
<div class="col">{{ form_row(form.proagdur_ecorec_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_pertes) }}
<div class="col">{{ form_widget(form.proagdur_pertes) }}</div>
<div class="col">{{ form_row(form.proagdur_pertes_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_climat) }}
<div class="col">{{ form_widget(form.proagdur_climat) }}</div>
<div class="col">{{ form_row(form.proagdur_climat_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_geneti) }}
<div class="col">{{ form_widget(form.proagdur_geneti) }}</div>
<div class="col">{{ form_row(form.proagdur_geneti_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.proagdur_prosol) }}
<div class="col">{{ form_widget(form.proagdur_prosol) }}</div>
<div class="col">{{ form_row(form.proagdur_prosol_comment) }}</div>
</div>
</div>
<div class="container global-evaluation-container">
<div class="row">
{{ form_label(form.proagdur_global) }}
<div class="col">{{ form_widget(form.proagdur_global) }}</div>
<div class="col">{{ form_row(form.proagdur_global_comment) }}</div>
</div>
</div>
{% if form.review_proagdur_global is defined %}
<br/>
<div class="container review-container">
<div class="row">
{{ form_label(form.review_proagdur_global) }}
<div class="col">{{ form_widget(form.review_proagdur_global) }}</div>
<div class="col">{{ form_row(form.review_proagdur_global_comment) }}</div>
</div>
</div>
{% endif %}
<br/>
<br/>
{% if form.temporary_save is defined %}
<div class="prestaquizz_temporary_save_container">
{{ form_row(form.temporary_save) }}
</div>
{% endif %}
{{ form_end(form) }}
\ No newline at end of file
{% if object.selfevalprestaquiz and object.selfevalprestaquiz.isSubmitted %}
<a class="btn btn-sm btn-default" href="{{ admin.generateObjectUrl('reviewprestaquiz', object) }}">
Revue QCM
</a>
{% endif %}
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
<h2>Vous avez terminé votre demande d'inscription, merci !</h2>
<h2>Nous vous contacterons prochainement.</h2>
{% endblock %}
\ No newline at end of file
......@@ -63,6 +63,7 @@ exp_vente_emlc_adherent_email_subject: 'Vente de monnaie numérique à un adhér
exp_vente_emlc_prestataire_email_subject: 'Vente de monnaie numérique à un prestataire'
exp_ticket_print_email_subject: 'Impression de billets'
exp_ticket_destroy_email_subject: 'Destruction de billets'
exp_don_adherent_email_subject: 'Mon don à la Caisse Alimentaire'
des_achat_monnaie_adherent_email_subject: 'Achat de monnaie numérique'
des_achat_monnaie_prestataire_email_subject: 'Achat de monnaie numérique'
des_demande_achat_monnaie_email_subject: 'Réception de votre demande d''achat de monnaie numérique'
......@@ -89,6 +90,8 @@ des_vente_adherent_email_subject: 'Vente de billets à un adhérent'
des_vente_prestataire_email_subject: 'Vente de billets à un prestataire'
des_vente_emlc_adherent_email_subject: 'Vente de monnaie numérique à un adhérent'
des_vente_emlc_prestataire_email_subject: 'Vente de monnaie numérique à un prestataire'
des_don_adherent_email_subject: 'Don à la Caisse Alimentaire'
des_application_cotisation_tav: 'Complément de cotisation'
confirmation-cotisation-title: 'Cotisation bien reçue !'
confirmation-cotisation-content: 'Cotisation bien reçue, merci !'
confirmation-cotisation-footer-cta: 'acheter de la monnaie locale numérique'
......@@ -127,3 +130,6 @@ des_vente_emlc: 'Achat monnaie numérique'
exp_vente_emlc: 'Vente monnaie numérique'
exp_ticket_print: 'Impression de billets'
exp_ticket_destroy: 'Destruction de billets'
exp_prelevement_cotisation_adherent_depassement_plafond_email_subject: 'Réduction de l''allocation après dépassement du plafond'
exp_prelevement_cotisation_adherent_correction_solde_email_subject: 'Correction de solde (réduction de l''allocation) suite à une erreur de cotisation'
des_reversement_cotisation_adherent_correction_solde_email_subject: 'Correction de solde (allocation complémentaire de la caisse) suite à une erreur de cotisation'
\ No newline at end of file
......@@ -1852,9 +1852,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001489:
version "1.0.30001491"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001491.tgz"
integrity sha512-17EYIi4TLnPiTzVKMveIxU5ETlxbSO3B6iPvMbprqnKh4qJsQGk5Nh1Lp4jIMAE0XfrujsJuWZAM3oJdMHaKBA==
version "1.0.30001589"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz"
integrity sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==
chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.2:
version "2.4.2"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment