Commit 749ccd19 by Julien Jorry

Merge branch 'solidoume' into 'master'

Solidoume

See merge request federation-kohinos/kohinos!2
parents c210e423 e8b0ff4b
# Update v2.3.0 (2022-03-29)
## Sécurité sociale alimentaire (Solidoume pour la doume)
- Nouveau paramètre global USE_SOLIDOUME pour déterminer si on veut utiliser et afficher le programme
- Interface d'admin avec :
- configuration des différents paramètres Solidoume (montant min, max, commission...) ainsi que les emails envoyés
- Liste des participants à Solidoume
# Update v2.2.5 (2022-03-25)
- ajout d'un paramètre obligatoire MLC_URL qui doit refléter l'url du site kohinos installé (exemple : https://doume.org) => utile pour l'installation de l'application
- Fix bug sur le rôle contact d'un groupe local => impossible d'afficher l'historique des transactions du groupe
......
2.2.5
\ No newline at end of file
2.3.0
\ No newline at end of file
......@@ -289,6 +289,12 @@ jQuery(document).ready(function() {
}
});
$(".paiementDate").toggle($('#formSolidoumeItem_isRecurrent').is(':checked'))
$('#formSolidoumeItem_amount').slider()
$('#formSolidoumeItem_isRecurrent').click(function() {
$(".paiementDate").toggle(this.checked);
});
// $('.js-datepicker').datepicker({
// closeText: 'Fermer',
// prevText: '<Préc',
......
monolog:
channels: ['cron']
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
cron:
type: stream
path: "%kernel.logs_dir%/cron-%kernel.environment%.log"
level: debug
channels: ["cron"]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
......
......@@ -35,3 +35,5 @@ fos_ck_editor:
provider: sonata.media.provider.image
context: default # Optional, to upload in a custom context
#format: my-big # Optional, media format or original size returned to editor
allowedContent: true
extraAllowedContent : '*(*)'
\ No newline at end of file
monolog:
channels: ['cron']
handlers:
cron:
type: stream
path: "%kernel.logs_dir%/cron-%kernel.environment%.log"
level: debug
channels: ["cron"]
main:
type: fingers_crossed
action_level: error
......
swiftmailer:
url: '%env(MAILER_URL)%'
spool: { type: 'memory' }
\ No newline at end of file
......@@ -135,6 +135,10 @@ services:
autowire: false
arguments: ["@doctrine.orm.entity_manager"]
app.twig.solidoume.extension:
class: App\Twig\SolidoumeExtension
autowire: true
app.listener.unitofwork:
class: App\Listener\UnitOfWorkListener
tags:
......@@ -576,6 +580,28 @@ services:
show_mosaic_button: false
public: true
admin.solidoume:
class: App\Admin\SolidoumeAdmin
arguments: [~, App\Entity\SolidoumeItem, ~]
tags:
- name: sonata.admin
manager_type: orm
group: "Solidoume"
label: "Solidoume"
show_mosaic_button: false
public: true
admin.solidoume.parameters:
class: App\Admin\SolidoumeParamAdmin
arguments: [~, App\Entity\SolidoumeParameter, App\Controller\SolidoumeParameterController]
tags:
- name: sonata.admin
manager_type: orm
group: "Paramètres Solidoume"
label: "Paramètres Solidoume"
show_mosaic_button: false
public: true
sonata.media.provider.csv:
class: App\Admin\ImportProvider
tags:
......@@ -667,3 +693,8 @@ services:
{ name: console.command, command: 'translation:update' }
# id: console.command.translation_update
class: App\Command\TranslationUpdateCommand
app.menu_listener:
class: App\EventListener\MenuBuilderListener
tags:
- { name: kernel.event_listener, event: sonata.admin.event.configure.menu.sidebar, method: addMenuItems }
\ No newline at end of file
......@@ -610,6 +610,11 @@ App\Entity\Rubrique:
media: '@media17'
App\Entity\GlobalParameter:
gpsolidoume:
name: "USE_SOLIDOUME"
description: "Utiliser la sécurité sociale alimentaire"
value: "1"
mandatory: 1
gp00:
name: "MLC_URL"
description: "URL complète d'accès au Kohinos"
......@@ -620,6 +625,11 @@ App\Entity\GlobalParameter:
description: 'Montant minimum de la cotisation des adhérents'
value: '10'
mandatory: 1
gp111:
name: "COTISATION_ADHERENT_DEFAULT"
description: 'Montant affiché par défaut de la cotisation des adhérents'
value: '10'
mandatory: 1
gp12:
name: "COTISATION_PRESTATAIRE"
description: 'Montant minimum de la cotisation des prestataires'
......
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,7 +3,7 @@
"app": {
"js": [
"/build/runtime.6ad5c9da.js",
"/build/app.88763754.js"
"/build/app.81717810.js"
],
"css": [
"/build/app.1fb37df4.css"
......
{
"build/app.css": "/build/app.1fb37df4.css",
"build/app.js": "/build/app.88763754.js",
"build/app.js": "/build/app.81717810.js",
"build/admin.css": "/build/admin.5dc0eea7.css",
"build/admin.js": "/build/admin.8a6adf4b.js",
"build/runtime.js": "/build/runtime.6ad5c9da.js",
......
(()=>{"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.8a6adf4b.js'},{'revision':'ad79405d542b397996a3079203114b42','url':'/build/admin.8a6adf4b.js.LICENSE.txt'},{'revision':null,'url':'/build/app.1fb37df4.css'},{'revision':null,'url':'/build/app.88763754.js'},{'revision':'a10f79da820c176a89222bb88ecdd580','url':'/build/app.88763754.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.5dc0eea7.css'},{'revision':null,'url':'/build/admin.8a6adf4b.js'},{'revision':'ad79405d542b397996a3079203114b42','url':'/build/admin.8a6adf4b.js.LICENSE.txt'},{'revision':null,'url':'/build/app.1fb37df4.css'},{'revision':null,'url':'/build/app.81717810.js'},{'revision':'a10f79da820c176a89222bb88ecdd580','url':'/build/app.81717810.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
......@@ -99,7 +99,7 @@ class CotisationPrestataireAdmin extends CotisationAdmin
$expediteurInfos = [
'label' => 'Prestataire',
'class' => Prestataire::class,
'choices' => $em->getRepository(Prestataire::class)->findBy(['enabled' => true, 'mlc' => false], ['raison' => 'ASC']),
'choices' => $em->getRepository(Prestataire::class)->findDefault(),
'placeholder' => 'Choisir un prestataire',
'required' => true,
];
......
......@@ -2,27 +2,20 @@
namespace App\Admin;
use App\Entity\Prestataire;
use App\Enum\GroupePrestaEnum;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\MediaBundle\Form\Type\MediaType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
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\TextType;
use Symfony\Component\Security\Core\Security;
/**
* Administration des tags/états des prestataires (rappel, suspendus, ...)
* Administration des tags/états des prestataires (rappel, suspendus, ...).
*
* KOHINOS : Outil de gestion de Monnaie Locale Complémentaire
*
* @author Julien Jorry <julien.jorry@gmail.com>
*/
class EtatprestataireAdmin extends AbstractAdmin
......@@ -53,25 +46,24 @@ class EtatprestataireAdmin extends AbstractAdmin
protected function configureFormFields(FormMapper $formMapper)
{
$user = $this->security->getUser();
$prestataires = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Prestataire::class)->findBy(array('enabled' => true, 'mlc' => false), array('raison'=> 'ASC'));
$groupepresta = $this->getSubject();
$formMapper
->with('Informations', ['class' => 'col-md-7'])
->add('name', TextType::class, array(
->add('name', TextType::class, [
'label' => 'Nom :',
'required' => true
))
->add('content', CKEditorType::class, array(
'required' => true,
])
->add('content', CKEditorType::class, [
'label' => 'Description :',
// 'config' => ['enterMode' => 'CKEDITOR.ENTER_BR'],
'required' => false
))
->add('enabled', CheckboxType::class, array(
'required' => false,
])
->add('enabled', CheckboxType::class, [
'label' => 'Visibilité prestataire ?',
'required' => false,
'label_attr' => array('class' => 'checkbox-inline')
))
'label_attr' => ['class' => 'checkbox-inline'],
])
->end()
;
}
......@@ -83,21 +75,21 @@ class EtatprestataireAdmin extends AbstractAdmin
{
unset($this->listModes['mosaic']);
$listMapper
->addIdentifier('name', null, array(
'label' => 'Nom'
))
->add('content', null, array(
->addIdentifier('name', null, [
'label' => 'Nom',
])
->add('content', null, [
'label' => 'Description',
'sortable' => false
))
->add('getPrestatairesCount', null, array(
'sortable' => false,
])
->add('getPrestatairesCount', null, [
'label' => 'Nombre de prestas',
'sortable' => false
))
->add('enabled', null, array(
'sortable' => false,
])
->add('enabled', null, [
'label' => 'Visibilité prestataire',
'editable' => true
))
'editable' => true,
])
;
}
......
......@@ -95,7 +95,7 @@ class GroupeprestataireAdmin extends AbstractAdmin
// if ($user && ($this->security->isGranted('ROLE_GESTION_GROUPE') || $this->security->isGranted('ROLE_CONTACT') || $this->security->isGranted('ROLE_TRESORIER')) && !empty($this->getRequest()->getSession()->get('_groupegere'))) {
// $prestataires = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Prestataire::class)->findByGroupeLocal($this->getRequest()->getSession()->get('_groupegere'));
// } else {
$prestataires = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Prestataire::class)->findBy(['enabled' => true, 'mlc' => false], ['raison' => 'ASC']);
$prestataires = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Prestataire::class)->findDefault();
// }
if ($user && ($this->security->isGranted('ROLE_SUPER_ADMIN') || $this->security->isGranted('ROLE_ADMIN_SIEGE'))) {
$formMapper
......
......@@ -62,7 +62,7 @@ class RubriqueAdmin extends AbstractAdmin
{
$rubrique = $this->getSubject();
$user = $this->security->getUser();
$prestataires = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Prestataire::class)->findBy(['enabled' => true, 'mlc' => false], ['raison' => 'ASC']);
$prestataires = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Prestataire::class)->findDefault();
// get the current Image instance
$imageHelp = null;
......
<?php
namespace App\Admin;
use App\Entity\Adherent;
use App\Entity\SolidoumeParameter;
use App\Entity\User;
use Knp\Menu\ItemInterface;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\Form\Type\DateTimeRangePickerType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
use Symfony\Component\Validator\Constraints\LessThanOrEqual;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Administration des participations à Solidoume.
*
* KOHINOS : Outil de gestion de Monnaie Locale Complémentaire
*
* @author Julien Jorry <julien.jorry@gmail.com>
*/
class SolidoumeAdmin extends AbstractAdmin
{
protected $baseRouteName = 'solidoume';
protected $baseRoutePattern = 'solidoume';
protected $security;
protected $datagridValues = [
// reverse order (default = 'ASC')
'_sort_order' => 'DESC',
// name of the ordered field (default = the model's id field, if any)
'_sort_by' => 'createdAt',
'_page' => 1,
'_per_page' => 100,
];
/**
* {@inheritdoc}
*/
protected function configureFormFields(FormMapper $formMapper)
{
$item = $this->getSubject();
$user = $this->getConfigurationPool()->getContainer()->get('security.token_storage')->getToken() ? $this->getConfigurationPool()->getContainer()->get('security.token_storage')->getToken()->getUser() : null;
$em = $this->getConfigurationPool()->getContainer()->get('doctrine');
$adherents = $em->getRepository(Adherent::class)->findOrderByName();
$cotisationMaximum = $em->getRepository(SolidoumeParameter::class)->getValueOf('maximum');
$cotisationMinimum = $em->getRepository(SolidoumeParameter::class)->getValueOf('minimum');
$formMapper
->add('user', HiddenType::class, [
'data' => $user,
'data_class' => null,
'entity_class' => User::class,
'em' => $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager(),
])
->add('adherent', EntityType::class, [
'class' => Adherent::class,
'choices' => $adherents,
// 'choice_label' => 'name',
'placeholder' => 'Choisir un adherent',
'choice_label' => function ($adherent) {
return $adherent->getUser() ? ($adherent->getUser()->getLastname() . ' ' . $adherent->getUser()->getFirstname() . ' (' . $adherent->getUser()->getEmail() . ')') : $adherent->__toString();
},
'required' => false,
'label' => false,
])
->add('amount', MoneyType::class, [
'label' => 'Montant en euro(s) entre ' . $cotisationMinimum . ' et ' . $cotisationMaximum,
'required' => true,
'scale' => 2,
'constraints' => [
new NotBlank(),
new LessThanOrEqual(['value' => $cotisationMaximum]),
new GreaterThanOrEqual(['value' => $cotisationMinimum]),
],
])
->add('paiementDate', ChoiceType::class, [
'label' => 'Date de prélèvement',
'required' => false,
'choices' => range(1, 28),
'choice_label' => function ($choice) {
return $choice;
},
])
->add('isRecurrent', CheckboxType::class, [
'label' => 'Paiement récurrent (tous les mois) ?',
'required' => false,
'label_attr' => ['class' => 'checkbox-inline'],
])
->add('isDonation', CheckboxType::class, [
'label' => 'Est-ce une donation (sans participation au programme) ?',
'required' => false,
'label_attr' => ['class' => 'checkbox-inline'],
])
->add('enabled', CheckboxType::class, [
'label' => 'Activé ?',
'required' => false,
'label_attr' => ['class' => 'checkbox-inline'],
])
;
}
/**
* {@inheritdoc}
*/
protected function configureListFields(ListMapper $listMapper)
{
unset($this->listModes['mosaic']);
$listMapper
->add('user', null, ['label' => 'Utilisateur'])
->add('adherent', null, ['label' => 'Adhérent'])
->add('amount', null, ['label' => 'Montant'])
->add('paiementDate', null, ['label' => 'Date de prélèvement'])
->add('isDonation', null, ['label' => 'Donation'])
->add('isRecurrent', null, ['label' => 'Récurrent'])
->add('createdAt', 'date', [
'pattern' => 'dd/MM/YYYY HH:mm',
'label' => 'Création',
])
->add('updatedAt', 'date', [
'pattern' => 'dd/MM/YYYY HH:mm',
'label' => 'Mis à jour',
])
->add('enabled', null, ['label' => 'Activé ?'])
->add('comments', null, [
'label' => 'Commentaires',
'editable' => true,
])
// ->addIdentifier('enabled', null, ['label' => 'Activé', 'datatype' => 'App.SolidoumeItem', 'template' => '@kohinos/bundles/SonataAdminBundle/Boolean/editable_boolean.html.twig'])
->add('_action', null, [
'actions' => [
'edit' => [],
],
]);
}
public function configure()
{
parent::configure();
$this->setTemplate('list', '@kohinos/bundles/SonataAdminBundle/CRUD/solidoume_list.html.twig');
}
protected function configureSideMenu(ItemInterface $menu, string $action, AdminInterface $childAdmin = null)
{
if (!$childAdmin && !in_array($action, ['list'])) {
return;
}
$menu->addChild('Paramètres', [
'uri' => $this->getConfigurationPool()->getContainer()->get('router')->generate('solidoumeparam_list', [], UrlGeneratorInterface::ABSOLUTE_URL),
])->setAttribute('icon', 'fa fa-cogs');
}
public function getBatchActions()
{
$actions = parent::getBatchActions();
unset($actions['delete']);
return $actions;
}
/**
* {@inheritdoc}
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper): void
{
$datagridMapper
->add('enabled', null, [
'label' => 'Activé',
'show_filter' => true,
'advanced_filter' => false,
])
->add('isRecurrent', null, [
'label' => 'Récurrent ?',
'advanced_filter' => false,
])
->add('isDonation', null, [
'label' => 'Donation ?',
'advanced_filter' => false,
])
->add('createdAt', 'doctrine_orm_datetime_range', [
'field_type' => DateTimeRangePickerType::class,
'label' => 'Date de création',
])
->add('updatedAt', 'doctrine_orm_datetime_range', [
'field_type' => DateTimeRangePickerType::class,
'label' => 'Date de mise à jour',
])
;
}
}
<?php
namespace App\Admin;
use Knp\Menu\ItemInterface;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Route\RouteCollection;
/**
* Administration des paramètres à Solidoume.
*
* KOHINOS : Outil de gestion de Monnaie Locale Complémentaire
*
* @author Julien Jorry <julien.jorry@gmail.com>
*/
class SolidoumeParamAdmin extends AbstractAdmin
{
protected $baseRouteName = 'solidoumeparam';
protected $baseRoutePattern = 'solidoumeparam';
protected $security;
public function getTemplate($name)
{
if ('list' == $name) {
return '@kohinos/admin/solidoumeparameter_list.html.twig';
}
return parent::getTemplate($name);
}
/**
* {@inheritdoc}
*/
protected function configureListFields(ListMapper $listMapper)
{
unset($this->listModes['mosaic']);
$listMapper
->addIdentifier('user', null, ['label' => 'Utilisateur'])
->addIdentifier('adherent', null, ['label' => 'Adhérent'])
->addIdentifier('amount', null, ['label' => 'Montant'])
->addIdentifier('paiementDate', null, ['label' => 'Date de prélèvement'])
->addIdentifier('enabled', null, ['label' => 'Activé', 'datatype' => 'App.SolidoumeItem', 'template' => '@kohinos/bundles/SonataAdminBundle/Boolean/editable_boolean.html.twig'])
->add('_action', null, [
'actions' => [
'edit' => [],
],
]);
}
protected function configureSideMenu(ItemInterface $menu, string $action, AdminInterface $childAdmin = null)
{
if (!$childAdmin && in_array($action, ['list'])) {
return;
}
}
protected function configureRoutes(RouteCollection $collection)
{
$collection->remove('create');
}
public function getBatchActions()
{
$actions = parent::getBatchActions();
unset($actions['delete']);
return $actions;
}
}
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Adherent;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use App\Entity\SolidoumeItem;
use App\Entity\SolidoumeParameter;
use App\Entity\TransactionAdherentPrestataire;
use App\Entity\TransactionPrestataireAdherent;
use App\Entity\User;
use App\Enum\CurrencyEnum;
use App\Enum\MoyenEnum;
use App\Utils\CustomEntityManager;
use App\Utils\OperationUtils;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Twig\Environment;
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class SolidoumeCommand extends Command
{
protected static $defaultName = 'kohinos:solidoume:execute';
protected $em;
protected $logger;
protected $mailer;
protected $templating;
protected $io;
protected $param;
protected $operationUtils;
protected $isTest;
protected $testExecute;
protected $itemsTest;
public function __construct(
CustomEntityManager $em,
LoggerInterface $cronLogger,
\Swift_Mailer $mailer,
Environment $templating,
OperationUtils $operationUtils
) {
$this->em = $em;
$this->logger = $cronLogger;
$this->mailer = $mailer;
$this->templating = $templating;
$this->operationUtils = $operationUtils;
$this->isTest = false;
$this->testExecute = false;
$this->itemsTest = null;
parent::__construct();
}
protected function configure()
{
$this
->setDescription('Sécurité sociale alimentaire : executer les opérations')
->setDefinition([
new InputOption('test', null, InputOption::VALUE_NONE, 'For testing purpose : only print testresult of reminder email and taking money the right day'),
new InputOption('execute', null, InputOption::VALUE_NONE, 'For testing purpose : execute program even if it\s not the correct date fo execution !'),
])
;
}
/**
* Execute program Solidoume
*
* @param InputInterface $input
* @param OutputInterface $output
* @return integer
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io = new SymfonyStyle($input, $output);
$this->param = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
if (empty($this->param)) {
$this->io->error('Sécurité sociale alimentaire non paramétrée !');
return 0;
}
$this->em->getConnection()->getConfiguration()->setSQLLogger(null);
$this->isTest = $input->getOption('test');
$this->testExecute = $input->getOption('execute');
$this->io->title('Start');
$this->executeReminders();
$this->executeTaking();
sleep(1);
$this->executeProgram();
$this->io->success('End');
$memoryUsage = memory_get_usage(true) / 1024 / 1024;
$this->io->text("Batch finished with memory: ${memoryUsage}M");
return 0;
}
/**
* Execute email reminder : If balance of account if not enough x days before date of paiement => send email
*/
private function executeReminders()
{
$this->io->title('START : Rappel par email');
if (!$this->isTest) {
$items = $this->em->getRepository(SolidoumeItem::class)->findBy(['enabled' => true]);
} else {
$items = $this->getItemsForTest();
}
foreach ($items as $item) {
if ($this->hasToExecuteReminders($item)) {
$amount = $item->getAmount();
$accountEmlc = $item->getAdherent()->getAccountWithCurrency(CurrencyEnum::CURRENCY_EMLC);
if (null != $accountEmlc) {
if ($accountEmlc->getBalance() < $amount) {
// L'adherent n'a pas assez d'argent sur son ecompte !
if ($this->isTest) {
$this->io->error('Balance : ' . $accountEmlc->getBalance() . ', montant : ' . $amount);
}
$this->sendReminder($item);
}
} else {
$this->io->error("L'adherent " . $adherent->__toString() . " n'a pas de compte emlc !");
}
}
}
}
/**
* Send email reminder
*
* @param SolidoumeItem $solidoumeItem
*/
private function sendReminder(SolidoumeItem $solidoumeItem)
{
$adherent = $solidoumeItem->getAdherent();
$this->io->success("Envoi de l'email de rappel à l'adhérent : " . $adherent->__toString());
$subject = $this->em->getRepository(SolidoumeParameter::class)->getValueOf('name') . ' : Votre compte n\'a pas un solde suffisant !';
$mail = (new \Swift_Message($subject))
->setFrom($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL))
->setTo($adherent->getUser()->getEmail())
->setBody(
$this->templating->render(
'@kohinos/email/solidoume/reminder.html.twig',
[
'subject' => $subject,
'item' => $solidoumeItem,
]
),
'text/html'
);
if (!$this->isTest) {
$this->mailer->send($mail);
}
}
/**
* Take money of participant of solidoume if date of today is same as paiementDate
*/
private function executeTaking()
{
$this->io->title('START : prélèvement du jour');
$nowDay = (new \DateTime('now', new \DateTimeZone('UTC')))->format('d');
$nowMonth = intval((new \DateTime('now', new \DateTimeZone('UTC')))->format('Ym'));
if (!$this->isTest) {
$items = $this->em->getRepository(SolidoumeItem::class)->findBy(['enabled' => true, 'paiementDate' => $nowDay]);
} else {
$items = $this->getItemsForTest();
}
try {
$em = $this->em;
$isTest = $this->isTest;
$io = $this->io;
$operationUtils = $this->operationUtils;
$callback = function () use ($em, $items, $isTest, $io, $nowDay, $nowMonth, $operationUtils) {
foreach ($items as $item) {
// Prélèvement des emlc sur chaque compte adhérent participant au programme
$amount = $item->getAmount();
$accountEmlc = $item->getAdherent()->getAccountWithCurrency(CurrencyEnum::CURRENCY_EMLC);
if (null != $accountEmlc
&& $item->getPaiementDate() == $nowDay
&& $item->getLastMonthPayed() != $nowMonth) {
if ($accountEmlc->getBalance() < $amount) {
$io->error($item->getAdherent()->__toString() . ' : Solde de l\'adherent inférieur au montant à prélevé (Solde : ' . $accountEmlc->getBalance() . ' eMLC) !');
$item->setEnabled(false);
if (!$isTest) {
$em->persist($item);
}
// @TODO : alerter l'utilisateur ?
} else {
$flux = new TransactionAdherentPrestataire();
$flux->setExpediteur($item->getAdherent());
$flux->setDestinataire($em->getRepository(Prestataire::class)->getPrestataireSolidoume());
$flux->setMontant($amount);
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$now = (new \Datetime('now'))->format('d/m/Y H:i:s');
$flux->setReference($em->getRepository(SolidoumeParameter::class)->getValueOf('name') . ' prélèvement en date du ' . $now);
$item->setLastMonthPayed($nowMonth);
if (!$isTest) {
$em->persist($flux);
$em->persist($item);
// Write operations for this flux !
$operationUtils->executeOperations($flux);
$em->flush();
} else {
}
$io->success("Prélèvement $amount\nFlux : " . $flux->__toString() . "\nItem : " . $item->__toString());
}
}
}
};
$this->em->transactional($callback);
} catch (\Exception $e) {
// @TODO : tmp
throw $e;
}
}
/**
* Execute solidoume repartition of money recolted if date of today is same as executionDate
*/
private function executeProgram()
{
$nowDay = (new \DateTime('now', new \DateTimeZone('UTC')))->format('d');
// @TODO : uncomment this !
if (!$this->testExecute && $nowDay != $this->param->getExecutionDate()) {
$this->io->warning("Ce n'est pas le jour d'execution du programme (" . $this->param->getExecutionDate() . ')');
} else {
$this->io->title('START : Répartition de la somme récoltée');
if (!$this->isTest) {
$items = $this->em->getRepository(SolidoumeItem::class)->findBy(['enabled' => true]);
} else {
$items = $this->getItemsForTest();
}
$total = 0;
$countPerson = 0;
$countParticipants = 0;
foreach ($items as $item) {
if ($item->isEnabled()) {
$total += $item->getAmount();
if (!$item->getIsDonation()) {
++$countParticipants;
}
++$countPerson;
}
}
$totalByParticipant = round((($total / $countParticipants) * ((100 - $this->param->getCommission()) / 100)), 2, PHP_ROUND_HALF_DOWN);
$this->io->success('Total de eMLC récolté : ' . $total . ' !');
$this->io->success('Nombre de personnes : ' . $countPerson . ' !');
$this->io->success('Nombre de participants au programme : ' . $countParticipants . ' !');
$this->io->success('Total par participants (sans commission) : ' . ($total / $countParticipants) . ' !');
$this->io->success('Total par participants (avec commission de ' . $this->param->getCommission() . '%) : ' . $totalByParticipant . ' !');
try {
$em = $this->em;
$isTest = $this->isTest;
$io = $this->io;
$operationUtils = $this->operationUtils;
$callback = function () use ($em, $items, $isTest, $io, $totalByParticipant, $operationUtils) {
foreach ($items as $item) {
if (!$item->getIsDonation() && $item->isEnabled()) {
// Envoi de l'emlc sur le compte de l'adherent
$flux = new TransactionPrestataireAdherent();
$flux->setExpediteur($em->getRepository(Prestataire::class)->getPrestataireSolidoume());
$flux->setDestinataire($item->getAdherent());
$flux->setMontant($totalByParticipant);
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$now = (new \Datetime('now'))->format('d/m/Y H:i:s');
$flux->setReference($em->getRepository(SolidoumeParameter::class)->getValueOf('name') . ' ' . $now);
if (!$isTest) {
$em->persist($flux);
// Write operations for this flux !
$operationUtils->executeOperations($flux);
$em->flush();
}
$io->success("Paiement solidoume\nFlux : " . $flux->__toString() . "\nItem : " . $item->__toString());
}
if (!$item->getIsRecurrent()) {
// Disabled all non recurrent paiement
$item->setEnabled(false);
if (!$isTest) {
$em->persist($item);
} else {
$io->text('Paiement non recurrent donc désactivé ! ' . $item->__toString());
}
}
}
if (!$isTest) {
$em->flush();
$em->clear();
}
};
$this->em->transactional($callback);
} catch (\Exception $e) {
$this->io->error($e->getMessage());
}
}
return null;
}
/**
* Has to execute reminder by email ? Check date of paiement choose by adherent and number of days before remind
*
* @param SolidoumeItem $item
* @return boolean
*/
private function hasToExecuteReminders(SolidoumeItem $item)
{
$nowDay = intval((new \DateTime('now', new \DateTimeZone('UTC')))->format('d'));
$reminderDays = $this->param->getReminderDays();
$executionDate = intval($item->getPaiementDate());
if ($nowDay > $executionDate) {
$d = new \DateTime('now');
$d->modify('first day of next month');
$nextExecution = $d->format('Ym') . ($executionDate < 10 ? '0' . $executionDate : $executionDate);
$reminingDate = (new \DateTime('+' . $reminderDays . ' days', new \DateTimeZone('UTC')))->format('Ymd');
if ($reminingDate == $nextExecution) {
return true;
}
} else {
$nextExecution = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Ym') . ($executionDate < 10 ? '0' . $executionDate : $executionDate);
$reminingDate = (new \DateTime('+' . $reminderDays . ' days', new \DateTimeZone('UTC')))->format('Ymd');
if ($reminingDate == $nextExecution) {
return true;
}
}
return false;
}
/**
* Get items for local testing purpose
* @return array
*/
private function getItemsForTest()
{
if (null == $this->itemsTest) {
$this->itemsTest = [
$this->getItemForTest(10, true, true, $this->em->getRepository(Adherent::class)->findOneByEmail('julien.jorry@gmail.com')),
$this->getItemForTest(10, false, true, $this->em->getRepository(Adherent::class)->findOneByEmail('julien.jorry+22@gmail.com')),
$this->getItemForTest(40, false, false, $this->em->getRepository(Adherent::class)->findOneByEmail('julien.jorry+123@gmail.com')),
$this->getItemForTest(50, false, true, $this->em->getRepository(Adherent::class)->findOneByEmail('julien.jorry+test2@gmail.com')),
$this->getItemForTest(60, false, true, $this->em->getRepository(Adherent::class)->findOneByEmail('julien.jorry+testadh@gmail.com')),
$this->getItemForTest(70, true, false, $this->em->getRepository(Adherent::class)->findOneByEmail('julien.jorry+adh@gmail.cm')),
];
}
return $this->itemsTest;
}
/**
* Get one SolidouItem for local testing purpose
* @param int $amount
* @param bool $donation
* @param bool $recurrent
* @param Adherent $adherent
* @return SolidoumeItem
*/
private function getItemForTest(int $amount, bool $donation, bool $recurrent, Adherent $adherent)
{
$item0 = new SolidoumeItem();
$item0->setAmount($amount);
$item0->setIsRecurrent($recurrent);
$item0->setIsDonation($donation);
$item0->setPaiementDate(7);
$item0->setAdherent($adherent);
$item0->setUser($this->em->getRepository(User::class)->findOneByEmail('julien.jorry@gmail.com'));
$item0->setLastMonthPayed(202203);
$item0->setComments('test');
return $item0;
}
}
......@@ -55,7 +55,7 @@ class AdhesionController extends AbstractController
} else {
$this->addFlash(
'error',
'Problème avec l\'adhésion, veuillez vérifier les informations du formulaire !'
'Veuillez vérifier les informations du formulaire !'
);
}
}
......
......@@ -2,16 +2,15 @@
namespace App\Controller;
use App\Entity\Prestataire;
use App\Entity\Comptoir;
use App\Entity\Groupeprestataire;
use App\Entity\Prestataire;
use Doctrine\ORM\EntityManagerInterface;
use Sonata\MediaBundle\Provider\Pool;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Sonata\MediaBundle\Twig\Extension\MediaExtension;
use Symfony\Component\HttpFoundation\Request;
class MapController extends AbstractController
{
......@@ -45,9 +44,9 @@ class MapController extends AbstractController
$comptoirs = $this->em->getRepository(Comptoir::class)->findByGeoloc($lat, $lon, $distance);
$groupeprestataires = $this->em->getRepository(Groupeprestataire::class)->findByGeoloc($lat, $lon, $distance);
} else {
$prestas = $this->em->getRepository(Prestataire::class)->findBy(array('enabled' => true, 'mlc' => false), array('raison'=> 'ASC'));
$comptoirs = $this->em->getRepository(Comptoir::class)->findBy(array('enabled' => true), array('name'=> 'ASC'));
$groupeprestataires = $this->em->getRepository(Groupeprestataire::class)->findBy(array('enabled' => true), array('name'=> 'ASC'));
$prestas = $this->em->getRepository(Prestataire::class)->findDefault();
$comptoirs = $this->em->getRepository(Comptoir::class)->findBy(['enabled' => true], ['name' => 'ASC']);
$groupeprestataires = $this->em->getRepository(Groupeprestataire::class)->findBy(['enabled' => true], ['name' => 'ASC']);
}
$data = [];
......@@ -55,13 +54,13 @@ class MapController extends AbstractController
foreach ($prestas as $presta) {
if (count($presta->getGeolocs()) > 0) {
foreach ($presta->getGeolocs() as $geolocp) {
if ($geolocp->isEnabled() && $geolocp->getGeoloc()->getLat() != null && $geolocp->getGeoloc()->getLon() != null) {
if ($geolocp->isEnabled() && null != $geolocp->getGeoloc()->getLat() && null != $geolocp->getGeoloc()->getLon()) {
$data[$count]['name'] = $presta->__toString();
$data[$count]['content'] = $presta->getDescription();
$data[$count]['web'] = $presta->getWeb();
$data[$count]['link'] = $this->router->generate('show_prestataire', ['slug' => $presta->getSlug()]);
$data[$count]['hours'] = $presta->getHoraires();
if ($presta->getMedia() != null) {
if (null != $presta->getMedia()) {
$data[$count]['thumbnail'] = $this->path($presta->getMedia(), 'small', $request);
}
$data[$count]['geolocs'][] = $geolocp;
......@@ -72,31 +71,31 @@ class MapController extends AbstractController
'url' => $this->router->generate('show_rubrique', ['slug' => $rub->getSlug()]),
];
}
if ($presta->getRubriques()[0]->getMedia() != null) {
if (null != $presta->getRubriques()[0]->getMedia()) {
$data[$count]['icon'] = $this->path($presta->getRubriques()[0]->getMedia(), 'preview', $request);
}
}
$count++;
++$count;
}
}
}
}
foreach ($comptoirs as $comptoir) {
if ($comptoir->getGeoloc() != null && $comptoir->getGeoloc()->getLat() != null && $comptoir->getGeoloc()->getLon() != null) {
if (null != $comptoir->getGeoloc() && null != $comptoir->getGeoloc()->getLat() && null != $comptoir->getGeoloc()->getLon()) {
$data[$count]['name'] = $comptoir->__toString();
$data[$count]['content'] = $comptoir->getContent();
$data[$count]['geolocs'][] = $comptoir->getGeoloc();
$data[$count]['link'] = $this->router->generate('show_comptoir', ['slug' => $comptoir->getSlug()]);
if ($comptoir->getMedia() != null) {
if (null != $comptoir->getMedia()) {
$data[$count]['thumbnail'] = $this->path($comptoir->getMedia(), 'small', $request);
}
$count++;
++$count;
}
}
foreach ($groupeprestataires as $groupeprestataire) {
if ($groupeprestataire->getGeoloc() != null && $groupeprestataire->getGeoloc()->getLat() != null && $groupeprestataire->getGeoloc()->getLon() != null) {
if (null != $groupeprestataire->getGeoloc() && null != $groupeprestataire->getGeoloc()->getLat() && null != $groupeprestataire->getGeoloc()->getLon()) {
$data[$count]['name'] = $groupeprestataire->__toString();
$data[$count]['content'] = $groupeprestataire->getContent();
$data[$count]['geolocs'][] = $groupeprestataire->getGeoloc();
......@@ -108,17 +107,17 @@ class MapController extends AbstractController
foreach ($groupeprestataire->getPrestataires() as $presta) {
$groupeprestaData = [
'name' => $presta->__toString(),
'link' => $this->router->generate('show_prestataire', ['slug' => $presta->getSlug()])
'link' => $this->router->generate('show_prestataire', ['slug' => $presta->getSlug()]),
];
if (count($presta->getRubriques()) > 0) {
if ($presta->getRubriques()[0]->getMedia() != null) {
if (null != $presta->getRubriques()[0]->getMedia()) {
$groupeprestaData['rubrique']['name'] = $presta->getRubriques()[0]->getName();
$groupeprestaData['rubrique']['icon'] = $this->path($presta->getRubriques()[0]->getMedia(), 'preview', $request);
}
}
$data[$count]['prestataires'][] = $groupeprestaData;
}
$count++;
++$count;
}
}
......@@ -136,6 +135,6 @@ class MapController extends AbstractController
$format = $provider->getFormatName($media, $format);
return $request->getSchemeAndHttpHost().$provider->generatePublicUrl($media, $format);
return $request->getSchemeAndHttpHost() . $provider->generatePublicUrl($media, $format);
}
}
......@@ -48,7 +48,7 @@ class PrestatairesController extends FrontController
return $this->redirectToRoute('index');
}
if ($order == 'groupelocal') {
if ('groupelocal' == $order) {
$prestas = $this->em->getRepository(Prestataire::class)->findDefault('prestataire', 'groupelocal');
return $this->render('@kohinos/presta/liste_prestataires_bygroupelocal.html.twig', [
......@@ -77,7 +77,7 @@ class PrestatairesController extends FrontController
return $this->render('@kohinos/presta/liste_prestataires.html.twig', [
'prestas' => $prestas,
'type' => 'Prestataires du groupe local '.$groupe->getName(),
'type' => 'Prestataires du groupe local ' . $groupe->getName(),
]);
}
......@@ -105,7 +105,7 @@ class PrestatairesController extends FrontController
if (!$this->isFrontActivated()) {
return $this->redirectToRoute('index');
}
$prestas = $this->em->getRepository(Prestataire::class)->findBy(['enabled' => true], ['raison' => 'ASC']);
$prestas = $this->em->getRepository(Prestataire::class)->findDefault();
return $this->render('@kohinos/presta/carte.html.twig', [
'prestas' => $prestas,
......
<?php
namespace App\Controller;
use App\Entity\GlobalParameter;
use App\Entity\SolidoumeItem;
use App\Entity\SolidoumeParameter;
use App\Form\Type\SolidoumeFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Twig\Environment;
class SolidoumeController extends AbstractController
{
private $em;
private $security;
private $mailer;
private $templating;
public function __construct(EntityManagerInterface $em, Security $security, \Swift_Mailer $mailer, Environment $templating)
{
$this->em = $em;
$this->security = $security;
$this->mailer = $mailer;
$this->templating = $templating;
}
/**
* @Route("/ssa/desinscription", name="solidoume_unsubscribe")
*/
public function unsubscribeAction(Request $request)
{
if (!(null != $this->security->getUser()->getAdherent() && $this->security->getUser()->isGranted('ROLE_ADHERENT'))) {
$this->addFlash(
'error',
'Page inaccessible, vous devez être connecté en adhérent !'
);
return $this->redirectToRoute('index');
}
$solidoumeItem = $this->em->getRepository(SolidoumeItem::class)->findOneBy(['adherent' => $this->security->getUser()->getAdherent(), 'enabled' => true]);
$solidoumeItem->setEnabled(false);
$this->em->persist($solidoumeItem);
$this->em->flush();
$solidoumeParam = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
$name = 'la sécurite sociale alimentaire';
if (null != $solidoumeParam) {
$name = $solidoumeParam->getName();
}
$this->addFlash(
'success',
'Désinscription à ' . $name . ' bien prise en compte !'
);
return $this->redirectToRoute('index');
}
/**
* @Route("/ssa/adherer", name="solidoume_adherer")
*/
public function adhererAction(Request $request)
{
if (!(null != $this->security->getUser()->getAdherent() && $this->security->getUser()->isGranted('ROLE_ADHERENT'))) {
$this->addFlash(
'error',
'Page inaccessible, vous devez être connecté en adhérent !'
);
return $this->redirectToRoute('index');
}
$solidoumeParam = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
if (empty($solidoumeParam)) {
$this->addFlash(
'error',
'Programme non configuré !'
);
return $this->redirectToRoute('index');
}
$data = $this->em->getRepository(SolidoumeItem::class)->findOneBy(['adherent' => $this->security->getUser()->getAdherent(), 'enabled' => true]);
$alreadyExist = false;
if (!empty($data)) {
$alreadyExist = true;
}
$form = $this->createForm(SolidoumeFormType::class, $data);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$solidoumeItem = $form->getData();
$this->em->persist($solidoumeItem);
$this->em->flush();
// if (!$alreadyExist) {
$this->sendMail($solidoumeItem);
// }
$this->addFlash(
'success',
$solidoumeParam->getName() . ' : Merci de votre participation !'
);
$referer = $request->request->get('referer');
if ($referer && !$request->isXmlHttpRequest()) {
return $this->redirect($referer);
} elseif (!$request->isXmlHttpRequest()) {
return new Response('', Response::HTTP_BAD_REQUEST);
}
}
return $this->render('@kohinos/solidoume/index.html.twig', [
'form' => $form->createView(),
'param' => $solidoumeParam,
]);
}
private function sendMail(SolidoumeItem $solidoumeItem)
{
$subject = $this->em->getRepository(SolidoumeParameter::class)->getValueOf('name');
$from = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL);
$mail = (new \Swift_Message($subject))
->setFrom($from)
->setTo($solidoumeItem->getUser()->getEmail())
->setBody(
$this->templating->render(
'@kohinos/email/solidoume/confirm.html.twig',
[
'subject' => $subject,
'item' => $solidoumeItem,
]
),
'text/html'
);
$this->mailer->send($mail);
}
}
<?php
namespace App\Controller;
use App\Entity\SolidoumeParameter;
use App\Form\Type\SolidoumeParameterFormType;
use App\Utils\CustomEntityManager;
use Ramsey\Uuid\Uuid;
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\Security\Core\Security;
class SolidoumeParameterController extends CRUDController
{
protected $em;
protected $security;
public function __construct(CustomEntityManager $em, Security $security)
{
$this->em = $em;
$this->security = $security;
}
/**
* List action.
*
* @throws AccessDeniedException If access is not granted
*
* @return Response
*/
public function listAction()
{
$request = $this->getRequest();
$data = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
$oldData = null;
if (!empty($data)) {
$oldData = clone $data;
}
$form = $this->createForm(SolidoumeParameterFormType::class, $data);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (null != $oldData) {
$oldData->setEnabled(false);
$this->em->persist($oldData);
$this->em->flush();
}
$solidoumeParameter = $form->getData();
$solidoumeParameter->setId(Uuid::uuid4());
$solidoumeParameter->setEnabled(true);
$this->em->persist($solidoumeParameter);
$this->em->flush();
$this->addFlash(
'sonata_flash_success',
'Paramètres de la sécurite sociale alimentaire bien validés !'
);
return $this->redirectToRoute('solidoume_list');
}
$template = $this->admin->getTemplate('list');
return $this->renderWithExtraParams($template, [
'action' => 'list',
'form' => $form->createView(),
], null);
}
}
......@@ -25,12 +25,15 @@ class GlobalParameter
const MLC_NOTIF_EMAIL = 'MLC_NOTIF_EMAIL';
const MLC_CONTACT_EMAIL = 'MLC_CONTACT_EMAIL';
const COTISATION_ADHERENT = 'COTISATION_ADHERENT';
const COTISATION_ADHERENT_DEFAULT = 'COTISATION_ADHERENT_DEFAULT';
const COTISATION_PRESTATAIRE = 'COTISATION_PRESTATAIRE';
const RECONVERSION_PRESTATAIRE = 'RECONVERSION_PRESTATAIRE';
const MAP_CENTER = 'MAP_CENTER';
const MAP_ZOOM = 'MAP_ZOOM';
const USE_PAYZEN = 'USE_PAYZEN';
const ALL_TICKETS = 'ALL_TICKETS';
const USE_SOLIDOUME = 'USE_SOLIDOUME';
const ADHESION_TEXT = 'ADHESION_TEXT';
/**
* @var \Ramsey\Uuid\UuidInterface
......@@ -43,7 +46,7 @@ class GlobalParameter
protected $id;
/**
* @ORM\Column(type="string", length=255)
* @ORM\Column(type="string", length=255, unique=true)
*/
private $name;
......
......@@ -170,6 +170,16 @@ class Prestataire extends AccountableObject implements AccountableInterface
private $mlc = false;
/**
* Le prestataire un peu spécial reçoit les participations au programme solidoume.
*
* @var bool
*
* @ORM\Column(name="solidoume", type="boolean", nullable=false, options={"default" : false})
* @Groups({"read"})
*/
private $solidoume = false;
/**
* Le prestataire accepte la monnaie électronique !
*
* @var bool
......@@ -523,6 +533,26 @@ class Prestataire extends AccountableObject implements AccountableInterface
}
/**
* @return bool
*/
public function isSolidoume(): bool
{
return $this->solidoume;
}
/**
* @param bool $solidoume
*
* @return Prestataire
*/
public function setSolidoume(bool $solidoume): self
{
$this->solidoume = $solidoume;
return $this;
}
/**
* Accepte la monnaie électronique.
*
* @return bool
......
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Entity\EntityTrait\EnablableEntityTrait;
use App\Repository\SolidoumeItemRepository;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\Doctrine\UuidGenerator;
/**
* @ApiResource()
* @ORM\Entity(repositoryClass=SolidoumeItemRepository::class)
*/
class SolidoumeItem
{
use EnablableEntityTrait;
use TimestampableEntity;
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
protected $id;
/**
* @ORM\Column(type="float")
*/
private $amount;
/**
* @ORM\Column(type="boolean")
*/
private $isRecurrent;
/**
* @ORM\Column(type="integer")
*/
private $paiementDate;
/**
* @ORM\Column(type="boolean")
*/
private $isDonation;
/**
* @ORM\ManyToOne(targetEntity="Adherent")
* @ORM\JoinColumn(name="adherent_id", referencedColumnName="id", nullable=false)
*/
private $adherent;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="news")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $user;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $comments;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $lastMonthPayed;
public function getId()
{
return $this->id;
}
public function getAmount(): ?float
{
return $this->amount;
}
public function setAmount(float $amount): self
{
$this->amount = $amount;
return $this;
}
public function getIsRecurrent(): ?bool
{
return $this->isRecurrent;
}
public function setIsRecurrent(bool $isRecurrent): self
{
$this->isRecurrent = $isRecurrent;
return $this;
}
public function getPaiementDate(): ?int
{
return $this->paiementDate;
}
public function setPaiementDate(int $paiementDate): self
{
$this->paiementDate = $paiementDate;
return $this;
}
public function getIsDonation(): ?bool
{
return $this->isDonation;
}
public function setIsDonation(bool $isDonation): self
{
$this->isDonation = $isDonation;
return $this;
}
public function getAdherent(): ?Adherent
{
return $this->adherent;
}
public function setAdherent(Adherent $adherent): self
{
$this->adherent = $adherent;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getComments(): ?string
{
return $this->comments;
}
public function setComments(?string $comments): self
{
$this->comments = $comments;
return $this;
}
public function getLastMonthPayed(): ?int
{
return $this->lastMonthPayed;
}
public function setLastMonthPayed(?int $lastMonthPayed): self
{
$this->lastMonthPayed = $lastMonthPayed;
return $this;
}
public function __toString()
{
return 'SolidoumeItem ' . ($this->getId() ? '#' . $this->getId() : '') . ', amount : ' . $this->getAmount() . ', adherent ' . $this->getAdherent()->__toString() . ', paiementDate : ' . $this->getPaiementDate() . ', lastMonthPayed : ' . $this->getLastMonthPayed() . ', don : ' . $this->getIsDonation() . ', recurrent : ' . $this->getIsRecurrent() . ', enabled : ' . $this->isEnabled();
}
}
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Entity\EntityTrait\EnablableEntityTrait;
use App\Repository\SolidoumeParameterRepository;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\Doctrine\UuidGenerator;
/**
* ApiResource().
*
* @ORM\Entity(repositoryClass=SolidoumeParameterRepository::class)
*/
class SolidoumeParameter
{
use EnablableEntityTrait;
use TimestampableEntity;
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
protected $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $description;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $reminderEmail;
/**
* @ORM\Column(type="float")
*/
private $minimum;
/**
* @ORM\Column(type="float")
*/
private $maximum;
/**
* @ORM\Column(type="float")
*/
private $commission;
/**
* @ORM\Column(type="integer")
*/
private $reminderDays;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $confirmEmail;
/**
* @ORM\Column(type="integer")
*/
private $executionDate;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="news")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $user;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getReminderEmail(): ?string
{
return $this->reminderEmail;
}
public function setReminderEmail(?string $reminderEmail): self
{
$this->reminderEmail = $reminderEmail;
return $this;
}
public function getMinimum(): ?float
{
return $this->minimum;
}
public function setMinimum(float $minimum): self
{
$this->minimum = $minimum;
return $this;
}
public function getMaximum(): ?float
{
return $this->maximum;
}
public function setMaximum(float $maximum): self
{
$this->maximum = $maximum;
return $this;
}
public function getCommission(): ?float
{
return $this->commission;
}
public function setCommission(float $commission): self
{
$this->commission = $commission;
return $this;
}
public function getReminderDays(): ?int
{
return $this->reminderDays;
}
public function setReminderDays(int $reminderDays): self
{
$this->reminderDays = $reminderDays;
return $this;
}
public function getConfirmEmail(): ?string
{
return $this->confirmEmail;
}
public function setConfirmEmail(?string $confirmEmail): self
{
$this->confirmEmail = $confirmEmail;
return $this;
}
public function getExecutionDate(): ?int
{
return $this->executionDate;
}
public function setExecutionDate(int $executionDate): self
{
$this->executionDate = $executionDate;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
<?php
// src/EventListener/MenuBuilderListener.php
namespace App\EventListener;
use App\Entity\GlobalParameter;
use App\Entity\SolidoumeParameter;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Menu\Util\MenuManipulator;
use Sonata\AdminBundle\Event\ConfigureMenuEvent;
final class MenuBuilderListener
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function addMenuItems(ConfigureMenuEvent $event): void
{
$menu = $event->getMenu();
$useSolidoume = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_SOLIDOUME);
$soliParam = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
$name = 'Sécurité sociale alimentaire';
if (!empty($soliParam)) {
$name = $soliParam->getName();
}
if ('true' == $useSolidoume) {
$child = $menu->addChild('reports', [
'label' => $name,
'route' => 'solidoume_list',
])->setExtras([
'icon' => '<i class="fa fa-handshake-o"></i>', // html is also supported
]);
$manipulator = new MenuManipulator();
$manipulator->moveToPosition($child, 7);
}
}
}
......@@ -30,14 +30,15 @@ class AddCotisationFormType extends AbstractType
{
$now = new \DateTime();
$cotisationMinimum = intval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_ADHERENT));
$cotisationDefault = intval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_ADHERENT_DEFAULT));
$builder
->add('reference', HiddenType::class, [
'required' => true,
'data' => 'Adhésion ' . $now->format('d/m/Y'),
])
->add('montant', IntegerType::class, [
'label' => 'Montant en prix libre (minimum ' . $cotisationMinimum . '€) :',
'data' => $cotisationMinimum,
'label' => 'Montant de la cotisation :',
'data' => $cotisationDefault,
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual(['value' => $cotisationMinimum]),
......
......@@ -38,16 +38,19 @@ class CotiserFormType extends AbstractType
throw new \Exception('Opération impossible ! Utilisateur non connecté !');
}
$now = new \DateTime();
$montant = 0;
$montantMinimum = 0;
$montantDefault = 0;
$canPayWithMlc = false;
if ($this->security->isGranted('ROLE_ADHERENT')) {
$montant = floatval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_ADHERENT));
$canPayWithMlc = ($this->security->getUser()->getAdherent()->getEmlcAccount()->getBalance() >= $montant);
$montantMinimum = floatval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_ADHERENT));
$montantDefault = floatval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_ADHERENT_DEFAULT));
$canPayWithMlc = ($this->security->getUser()->getAdherent()->getEmlcAccount()->getBalance() >= $montantMinimum);
} elseif ($this->security->isGranted('ROLE_PRESTATAIRE')) {
$montant = floatval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_PRESTATAIRE));
$montantMinimum = floatval($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::COTISATION_PRESTATAIRE));
$montantDefault = $montantMinimum;
$presta = $this->session->get('_prestagere');
$presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId());
$canPayWithMlc = (($presta->getEmlcAccount())->getBalance() >= $montant);
$canPayWithMlc = (($presta->getEmlcAccount())->getBalance() >= $montantMinimum);
}
$canPayWithCB = 'true' === $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_PAYZEN) ? true : false;
......@@ -74,10 +77,11 @@ class CotiserFormType extends AbstractType
'data' => 'Cotisation ' . $now->format('Y'),
])
->add('montant', IntegerType::class, [
'data' => $montant,
'label' => 'Montant de la cotisation :',
'data' => $montantDefault,
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual(['value' => $montant]),
new GreaterThanOrEqual(['value' => $montantMinimum]),
],
])
->add('moyen', HiddenType::class, [
......
......@@ -70,6 +70,17 @@ class GlobalConfigurationFormType extends AbstractType
'name_param' => GlobalParameter::COTISATION_ADHERENT,
'help' => 'Ne pas utiliser de virgule ",", utiliser le point "."',
'required' => true,
'_placeholder' => '5',
'constraints_param' => [
new TypeConstraint(['type' => 'numeric', 'message' => "Le montant n'est pas valide !"]),
],
])
->add('mlccotisationadhdefault', GlobalParameterType::class, [
'label' => 'Montant affiché par défaut de la cotisation des adhérents :',
'_description' => 'Montant affiché par défaut de la cotisation des adhérents',
'name_param' => GlobalParameter::COTISATION_ADHERENT_DEFAULT,
'help' => 'Ne pas utiliser de virgule ",", utiliser le point "."',
'required' => true,
'_placeholder' => '10',
'constraints_param' => [
new TypeConstraint(['type' => 'numeric', 'message' => "Le montant n'est pas valide !"]),
......@@ -124,7 +135,6 @@ class GlobalConfigurationFormType extends AbstractType
])
// @TODO : checkbox au lieu de texttype pour certains paramètres globaux
->add('usewordpress', GlobalParameterType::class, [
'label' => false,
'_description' => 'Utiliser le module Wordpress et désactiver les fonctions du site kohinos (ne pas cocher, pas encore développé)',
'label' => 'Utiliser Wordpress pour le site web (pas encore développé)',
'name_param' => GlobalParameter::USE_WORDPRESS,
......@@ -138,7 +148,6 @@ class GlobalConfigurationFormType extends AbstractType
'help' => 'Si cette option est à "true", le site web public du Kohinos sera désactivé, seul la page de connexion et l\'administration seront accesssible !',
])
->add('usepayzen', GlobalParameterType::class, [
'label' => false,
'_description' => 'Utiliser la module de paiement Payzen',
'name_param' => GlobalParameter::USE_PAYZEN,
'label' => 'Utiliser le paiement par CB avec Payzen',
......@@ -150,6 +159,26 @@ class GlobalConfigurationFormType extends AbstractType
// 'label_attr' => ['class' => 'checkbox-inline'], ],
'help' => 'Si cette option est à "true", les utilisateurs pourront cotiser et acheter de la monnaie locale par CB sur l\'application (compte Payzen activé obligatoire).',
])
->add('usesolidoume', GlobalParameterType::class, [
'required' => true,
'_description' => 'Utiliser la sécurité sociale alimentaire',
'name_param' => GlobalParameter::USE_SOLIDOUME,
'label' => 'Utiliser la sécurité sociale alimentaire',
'_placeholder' => 'false',
'_data' => 'false',
// '_type' => CheckboxType::class,
// '_type_options' => ['label' => 'Utiliser le paiement par CB avec Payzen',
// 'required' => false,
// 'label_attr' => ['class' => 'checkbox-inline'], ],
'help' => 'Si cette option est à "true", les utilisateurs pourront accéder et participer au programme de Sécurité Sociale alimentaire',
])
->add('adhesiontext', GlobalParameterType::class, [
'required' => false,
'_description' => "Description présente sur la page d'adhésion",
'name_param' => GlobalParameter::ADHESION_TEXT,
'label' => "Description présente sur la page d'adhésion",
'help' => 'Si cette option est à "true", les utilisateurs pourront accéder et participer au programme de Sécurité Sociale alimentaire',
])
->add('allTickets', GlobalParameterType::class, [
'label' => 'Valeur des différents montants des billets de MLC :',
'_description' => 'Valeur des différents montants des billets de MLC (exemple : 1,2,5,10,20,50)',
......
<?php
namespace App\Form\Type;
use App\Entity\Adherent;
use App\Entity\SolidoumeItem;
use App\Entity\SolidoumeParameter;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\RangeType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
class SolidoumeFormType extends AbstractType
{
protected $em;
protected $security;
private $defaultMontantSlider = 0;
private $minMontantSlider = 0;
private $maxMontantSlider = 0;
public function __construct(EntityManagerInterface $em, Security $security)
{
$this->em = $em;
$this->security = $security;
$this->minMontantSlider = $this->em->getRepository(SolidoumeParameter::class)->getValueOf('minimum');
$this->defaultMontantSlider = $this->minMontantSlider;
$this->maxMontantSlider = $this->em->getRepository(SolidoumeParameter::class)->getValueOf('maximum');
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (null == $this->security->getUser() || null == $this->security->getUser()->getAdherent() || !$this->security->getUser()->isGranted('ROLE_ADHERENT')) {
throw new \Exception('[SOLIDOUME] Opération impossible, vous n\'êtes pas connecté en tant qu\'adhérent !');
}
$data = isset($options['data']) ? $options['data'] : null;
if (null == $data) {
$adherent = $this->security->getUser()->getAdherent();
$amount = $this->defaultMontantSlider;
} else {
$adherent = $data->getAdherent();
$amount = $data->getAmount();
}
$rangetickValue = implode(', ', range($this->minMontantSlider, $this->maxMontantSlider, 5));
$rangetick = range(0, 100, 100 / (count(range($this->minMontantSlider, $this->maxMontantSlider, 5)) - 1));
$rangetickRounded = [];
foreach ($rangetick as $k => $r) {
$rangetickRounded[$k] = floor($r);
}
$rangetickRounded[count($rangetickRounded) - 1] = 100;
$rangetickString = '[' . implode(', ', $rangetickRounded) . ']';
$builder
->add('user', HiddenType::class, [
'data_class' => null,
'data' => $this->security->getUser(),
'entity_class' => User::class,
'em' => $this->em,
])
->add('adherent', HiddenType::class, [
'data_class' => null,
'data' => $adherent,
'entity_class' => Adherent::class,
'em' => $this->em,
])
->add('amount', RangeType::class, [
'attr' => [
'min' => $this->minMontantSlider,
'max' => $this->maxMontantSlider,
'data-provide' => 'slider',
'data-slider-ticks' => '[' . $rangetickValue . ']',
'data-slider-ticks-labels' => '[' . $rangetickValue . ']',
'data-slider-ticks-positions' => $rangetickString,
'data-slider-min' => $this->minMontantSlider,
'data-slider-max' => $this->maxMontantSlider,
'data-slider-step' => '5',
'data-slider-value' => $amount,
'style' => 'width:100%;',
'class' => 'solidoume-montant-slider',
],
'label' => 'Montant : ',
'required' => false,
])
->add('isRecurrent', CheckboxType::class, [
'required' => false,
'label' => 'Paiement récurrent tous les mois ?',
])
->add('paiementDate', ChoiceType::class, [
'label' => 'Date de prélèvement',
'required' => false,
'choices' => range(1, 28),
'choice_label' => function ($choice) {
return $choice;
},
'row_attr' => ['style' => 'display:none', 'class' => 'paiementDate'],
'attr' => ['class' => 'w-50 m-auto'],
])
->add('isDonation', CheckboxType::class, [
'required' => false,
'label' => 'Je ne souhaite pas recevoir de contrepartie',
])
->add('save', SubmitType::class, ['label' => 'Valider'])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SolidoumeItem::class,
'cascade_validation' => true,
]);
}
public function getBlockPrefix()
{
return 'formSolidoumeItem';
}
}
<?php
namespace App\Form\Type;
use App\Entity\SolidoumeParameter;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\AbstractType;
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\NumberType;
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\Security\Core\Security;
class SolidoumeParameterFormType extends AbstractType
{
protected $em;
protected $security;
public function __construct(EntityManagerInterface $em, Security $security)
{
$this->em = $em;
$this->security = $security;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', HiddenType::class, [
'data' => $this->security->getUser(),
'data_class' => null,
'entity_class' => User::class,
'em' => $this->em,
])
->add('name', TextType::class, [
'label' => 'Nom du programme',
'required' => true,
])
->add('description', CKEditorType::class, [
'label' => 'Description du programme',
'required' => false,
])
->add('minimum', NumberType::class, [
'label' => 'Montant minimum',
'required' => true,
])
->add('maximum', NumberType::class, [
'label' => 'Montant maximum',
'required' => true,
])
->add('commission', NumberType::class, [
'label' => 'Commission (en %)',
'required' => true,
'attr' => ['placeholder' => '0.0'],
])
->add('executionDate', ChoiceType::class, [
'label' => 'Date de prélèvement',
'required' => false,
'choices' => range(1, 28),
'choice_label' => function ($choice) {
return $choice;
},
'attr' => ['class' => 'w-50 m-auto'],
])
->add('reminderDays', IntegerType::class, [
'label' => 'Nombre de jour(s) avant prélèvement pour envoyer la relance si solde insuffisant',
'required' => true,
'attr' => ['placeholder' => '3'],
])
->add('reminderEmail', CKEditorType::class, [
'label' => 'Email de relance',
'required' => false,
'help' => 'Email reçu par le participant x jours avant l\'execution du programme seulement s\'il n\'a pas le solde suffisant sur son compte<br/>
Vous pouvez utilisez ces variables dans le text de l\'email :<br/>
<p class="ml-5">%name% : nom du programme</p>
<p class="ml-5">%adherent_name% : Nom de l\'adhérent</p>
<p class="ml-5">%amount% : Montant de sa participation</p>
<p class="ml-5">%paiementDate% : Jour (numéro) de son paiement</p>
<p class="ml-5">%executionDate% : Jour d\execution du programme</p>',
'help_html' => true,
])
->add('confirmEmail', CKEditorType::class, [
'label' => 'Email de confirmation de participation au programme',
'help' => 'Email reçu par le participant après avoir validé le formulaire<br/>
Vous pouvez utilisez ces variables dans le text de l\'email :<br/>
<p class="ml-5">%name% : nom du programme</p>
<p class="ml-5">%adherent_name% : Nom de l\'adhérent</p>
<p class="ml-5">%amount% : Montant de sa participation</p>
<p class="ml-5">%paiementDate% : Jour (numéro) de son paiement</p>
<p class="ml-5">%executionDate% : Jour d\execution du programme</p>',
'help_html' => true,
'required' => false,
])
->add('save', SubmitType::class, ['label' => 'Valider'])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SolidoumeParameter::class,
'cascade_validation' => true,
]);
}
public function getBlockPrefix()
{
return 'formSolidoumeParameter';
}
}
<?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 Version20220329182243 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 solidoume_item (id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', adherent_id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', user_id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', amount DOUBLE PRECISION NOT NULL, is_recurrent TINYINT(1) NOT NULL, paiement_date INT NOT NULL, is_donation TINYINT(1) NOT NULL, enabled TINYINT(1) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX IDX_E1D50D5925F06C53 (adherent_id), INDEX IDX_E1D50D59A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE solidoume_parameter (id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, reminder_email LONGTEXT DEFAULT NULL, minimum DOUBLE PRECISION NOT NULL, maximum DOUBLE PRECISION NOT NULL, commission DOUBLE PRECISION NOT NULL, reminder_days INT NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_general_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE solidoume_item ADD CONSTRAINT FK_E1D50D5925F06C53 FOREIGN KEY (adherent_id) REFERENCES adherent (id)');
$this->addSql('ALTER TABLE solidoume_item ADD CONSTRAINT FK_E1D50D59A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
$this->addSql('ALTER TABLE solidoume_parameter ADD confirm_email LONGTEXT DEFAULT NULL');
$this->addSql('ALTER TABLE solidoume_parameter ADD execution_date INT NOT NULL');
$this->addSql('ALTER TABLE solidoume_parameter ADD user_id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', ADD enabled TINYINT(1) NOT NULL');
$this->addSql('ALTER TABLE solidoume_parameter ADD CONSTRAINT FK_B4CCE5E1A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
$this->addSql('CREATE INDEX IDX_B4CCE5E1A76ED395 ON solidoume_parameter (user_id)');
$this->addSql('ALTER TABLE prestataire ADD solidoume TINYINT(1) DEFAULT \'0\' NOT NULL');
$this->addSql("INSERT INTO global_parameter (id, name, description, value, mandatory) VALUES (UUID(), 'USE_SOLIDOUME', 'Utiliser la sécurité sociale alimentaire', 'false', '1')");
$this->addSql('ALTER TABLE solidoume_item ADD comments LONGTEXT DEFAULT NULL');
$this->addSql('ALTER TABLE solidoume_item ADD last_month_payed INT DEFAULT NULL');
$this->addSql("INSERT INTO prestataire (id, media_id, raison, description, slug, metier, statut, responsable, iban, siret, web, mlc, horaires, enabled, created_at, updated_at, ecompte, tauxreconversion, groupe_id, typeprestataire_id, acceptemlc, idmlc, comments, solidoume) VALUES (UUID(), NULL, 'Sécurité Sociale Alimentaire', 'Sécurité Sociale Alimentaire', 'securite-sociale-alimentaire', NULL, NULL, NULL, NULL, NULL, NULL, '0', NULL, '0', CURRENT_DATE(), CURRENT_DATE(), '0.00', NULL, NULL, NULL, '0', NULL, 'Sécurité Sociale Alimentaire', '1')");
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE solidoume_parameter');
$this->addSql('DROP TABLE solidoume_item');
$this->addSql('ALTER TABLE prestataire DROP solidoume');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use App\Entity\AccountPrestataire;
use App\Entity\Prestataire;
use App\Enum\CurrencyEnum;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220329182822 extends AbstractMigration implements ContainerAwareInterface
{
/**
* @var ObjectManager
*/
protected $em;
public function setContainer(ContainerInterface $container = null)
{
$this->em = $container->get('doctrine')->getManager();
}
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
}
public function postUp(Schema $schema): void
{
$presta = $this->em->getRepository(Prestataire::class)->getPrestataireSolidoume();
// this up() migration is auto-generated, please modify it to your needs
$account = new AccountPrestataire();
$account
->setBalance(0)
->setCurrency(CurrencyEnum::CURRENCY_EMLC)
;
$presta->addAccount($account);
$this->em->persist($account);
$this->em->persist($presta);
$this->em->flush();
}
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 Version20220331123538 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 UNIQUE INDEX UNIQ_F696FA895E237E06 ON global_parameter (name)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX UNIQ_F696FA895E237E06 ON global_parameter');
}
}
<?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 Version20220407110424 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(), 'ADHESION_TEXT', 'Description présente sur la page d\'adhésion', '', '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 Version20220407111236 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(), 'COTISATION_ADHERENT_DEFAULT', 'Montant affiché par défaut de la cotisation des adhérents', '10', '1')");
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
}
}
......@@ -19,6 +19,21 @@ class AdherentRepository extends ServiceEntityRepository
parent::__construct($registry, Adherent::class);
}
public function findOneByEmail($email)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftjoin('p.user', 'u')
->where('p.enabled = :enabled')
->andWhere('u.email = :email')
->setParameter('enabled', true)
->setParameter('email', $email)
->getQuery()
->getSingleResult()
;
}
/**
* @return Adherent[] Returns an array of Adherent objects
*/
......
......@@ -31,11 +31,11 @@ class PrestataireRepository extends ServiceEntityRepository
{
$qb = $this->createQueryBuilder('p');
$qb = $this->addDefaultFilter($qb);
if ($orderBy == 'groupelocal' && !empty($direction) && ('ASC' === $direction || 'DESC' === $direction)) {
if ('groupelocal' == $orderBy && !empty($direction) && ('ASC' === $direction || 'DESC' === $direction)) {
$qb->leftJoin('p.groupe', 'g')
->orderBy('g.name', $direction)
->addOrderBy('p.raison', 'ASC');
} else if (!empty($orderBy) && !empty($direction) && ('ASC' === $direction || 'DESC' === $direction)) {
} elseif (!empty($orderBy) && !empty($direction) && ('ASC' === $direction || 'DESC' === $direction)) {
$qb->orderBy('p.' . $orderBy, $direction);
}
if (null != $limit) {
......@@ -66,6 +66,26 @@ class PrestataireRepository extends ServiceEntityRepository
return $return;
}
public function getPrestataireSolidoume()
{
if ($this->getEntityManager()->getFilters()->isEnabled('enabled_filter')) {
$this->getEntityManager()->getFilters()->disable('enabled_filter');
}
$qb = $this->createQueryBuilder('p');
$return = $qb
->andWhere('p.solidoume = 1')
// ->setParameter('solidoume', true)
->getQuery()
->getOneOrNullResult()
;
if (!$this->getEntityManager()->getFilters()->isEnabled('enabled_filter')) {
$this->getEntityManager()->getFilters()->enable('enabled_filter');
}
return $return;
}
public function getPrestataireMLC()
{
if ($this->getEntityManager()->getFilters()->isEnabled('enabled_filter')) {
......@@ -74,7 +94,6 @@ class PrestataireRepository extends ServiceEntityRepository
$qb = $this->createQueryBuilder('p');
$return = $qb
->leftJoin('p.geolocs', 'g')
->andWhere('p.mlc = :mlc')
->setParameter('mlc', true)
->getQuery()
......@@ -87,7 +106,7 @@ class PrestataireRepository extends ServiceEntityRepository
return $return;
}
public function addDefaultFilter(QueryBuilder $qb)
private function addDefaultFilter(QueryBuilder $qb)
{
$expr = $this->getEntityManager()->getExpressionBuilder();
if ($this->getEntityManager()->getFilters()->isEnabled('enabled_filter')) {
......@@ -95,10 +114,9 @@ class PrestataireRepository extends ServiceEntityRepository
}
$qb
// ->leftJoin('p.etats', 'tt')
->andWhere('p.mlc = :mlc')
->andWhere('p.solidoume = :solidoume')
->andWhere('p.enabled = :enabled')
// ->andWhere('tt.enabled != :disabled OR tt IS NULL')
->andWhere(
$expr->notIn(
'p.id',
......@@ -112,6 +130,7 @@ class PrestataireRepository extends ServiceEntityRepository
)
)
->setParameter('mlc', false)
->setParameter('solidoume', false)
->setParameter('enabled', true)
->setParameter('disabled', false)
;
......@@ -137,7 +156,9 @@ class PrestataireRepository extends ServiceEntityRepository
->leftJoin('p.geolocs', 'gs')
->leftJoin('gs.geoloc', 'g')
->andWhere('p.mlc = :mlc')
->andWhere('p.solidoume = :solidoume')
->setParameter('mlc', false)
->setParameter('solidoume', false)
->andWhere('' . $sqlDistance . ' < :distance')
->setParameter('distance', $distance)
->orderBy('p.raison', 'ASC')
......
<?php
namespace App\Repository;
use App\Entity\SolidoumeItem;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method SolidoumeItem|null find($id, $lockMode = null, $lockVersion = null)
* @method SolidoumeItem|null findOneBy(array $criteria, array $orderBy = null)
* @method SolidoumeItem[] findAll()
* @method SolidoumeItem[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class SolidoumeItemRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, SolidoumeItem::class);
}
}
<?php
namespace App\Repository;
use App\Entity\SolidoumeParameter;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method SolidoumeParameter|null find($id, $lockMode = null, $lockVersion = null)
* @method SolidoumeParameter|null findOneBy(array $criteria, array $orderBy = null)
* @method SolidoumeParameter[] findAll()
* @method SolidoumeParameter[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class SolidoumeParameterRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, SolidoumeParameter::class);
}
/**
* @return SolidoumeParameter[] Returns an array of SolidoumeParameter objects
*/
public function findTheOne()
{
return $this->findOneBy(['enabled' => true]);
}
public function getValueOf($param)
{
$solidoumeParam = $this->findTheOne();
if (!empty($solidoumeParam)) {
$action = 'get' . ucwords($param);
if (is_callable([$solidoumeParam, $action])) {
return $solidoumeParam->$action();
}
}
return null;
}
}
......@@ -3,6 +3,7 @@
namespace App\Twig;
use App\Entity\GlobalParameter;
use App\Entity\SolidoumeParameter;
use Doctrine\ORM\EntityManager;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
......@@ -28,6 +29,13 @@ class MlcGlobalsExtension extends AbstractExtension implements GlobalsInterface
$arrayGlobals['KOH_MAP_ZOOM'] = '6';
}
// $solidoumeParam = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
// if (!empty($solidoumeParam)) {
// foreach ($solidoumeParam as $key => $param) {
// $arrayGlobals['SD_'.$key] = $param;
// }
// }
return $arrayGlobals;
}
}
<?php
namespace App\Twig;
use App\Entity\SolidoumeItem;
use App\Entity\SolidoumeParameter;
use App\Form\Type\SolidoumeFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Twig\Extension\AbstractExtension;
class SolidoumeExtension extends AbstractExtension
{
protected $em;
protected $formFactory;
protected $router;
protected $security;
public function __construct(
EntityManagerInterface $em,
FormFactoryInterface $formFactory,
RouterInterface $router,
Security $security
) {
$this->em = $em;
$this->formFactory = $formFactory;
$this->router = $router;
$this->security = $security;
}
/**
* {@inheritdoc}
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('getSolidoumeParam', [$this, 'getSolidoumeParam']),
new \Twig_SimpleFunction('getSolidoumeForm', [$this, 'getSolidoumeForm']),
new \Twig_SimpleFunction('getSolidoumeParticipation', [$this, 'getSolidoumeParticipation']),
new \Twig_SimpleFunction('getSolidoumeDatas', [$this, 'getSolidoumeDatas']),
];
}
public function getSolidoumeParam(?string $name)
{
return $this->em->getRepository(SolidoumeParameter::class)->getValueOf($name);
}
public function getSolidoumeForm()
{
if (null == $this->security->getUser() || null == $this->security->getUser()->getAdherent() || !$this->security->getUser()->isGranted('ROLE_ADHERENT')) {
return null;
}
$data = $this->em->getRepository(SolidoumeItem::class)->findOneBy(['enabled' => true, 'adherent' => $this->security->getUser()->getAdherent()]);
$form = $this->formFactory->create(SolidoumeFormType::class, $data, ['action' => $this->router->generate('solidoume_adherer')]);
return $form->createView();
}
public function getSolidoumeParticipation()
{
if (null == $this->security->getUser() || null == $this->security->getUser()->getAdherent() || !$this->security->getUser()->isGranted('ROLE_ADHERENT')) {
return null;
}
$data = $this->em->getRepository(SolidoumeItem::class)->findOneBy(['enabled' => true, 'adherent' => $this->security->getUser()->getAdherent()]);
return $data;
}
public function getSolidoumeDatas()
{
$param = $this->em->getRepository(SolidoumeParameter::class)->findTheOne();
if (empty($param)) {
return null;
}
$items = $this->em->getRepository(SolidoumeItem::class)->findBy(['enabled' => true]);
if (empty($items)) {
return null;
}
$total = 0;
$countPerson = 0;
$countParticipants = 0;
foreach ($items as $item) {
$total += $item->getAmount();
if (!$item->getIsDonation()) {
++$countParticipants;
}
++$countPerson;
}
return [
'total' => $total,
'countPersons' => $countPerson,
'countDonateurs' => ($countPerson - $countParticipants),
'countParticipants' => $countParticipants,
'totalByParticipant' => ($total / $countParticipants),
'totalByParticipantWithCommission' => (($total / $countParticipants) * ((100 - $param->getCommission()) / 100)),
'commission' => $param->getCommission(),
];
}
}
......@@ -5,17 +5,18 @@
<div class='text-center mb-5 mt-2'>
<h4 class='mt-3'><b>{{'Adhérer'|trans }}</b></h4>
</div>
<p>
{% if isPayzenEnabled() %}
<p>
{% if KOH_ADHESION_TEXT|default('') != '' %}
{{ KOH_ADHESION_TEXT }}
{% endif %}
</p>
{{form_start(form)}}
{{ form_row(form.user) }}
{% if form.groupe is defined %}
{{ form_row(form.groupe) }}
{% endif %}
{{ form_row(form.geoloc) }}
<div class='text-center mb-5 mt-2'>
<h5><b>{{ 'Cotisation'|trans }}</b></h5>
</div>
{{ form_row(form.cotisation) }}
<div class='text-center mb-5 mt-2'>
{{ form_row(form.save) }}
......@@ -26,6 +27,5 @@
<p><i>Vous ne pouvez malheureusement pas adhérer ici, le paiement n'est pas configuré sur cette application.</i></p>
</div>
{% endif %}
</p>
</div>
{% endblock %}
{% extends '@kohinos/block/block_collapse.html.twig' %}
{% block blocktitle %}
<i class="fa fa-user-tie mr-4"></i> {{ getSolidoumeParam('name') }}
{% endblock blocktitle %}
{% block blocksubtitle %}
{% endblock blocksubtitle %}
{% block blockcontent %}
{% set participation = getSolidoumeParticipation() %}
{% if participation == null %}
<p>{{ getSolidoumeParam('description')|raw }}</p><br/>
<a class='btn btn-primary' href='{{ path('solidoume_adherer') }}'>{{ "Participer"|trans }}</a>
{% else %}
<p>Vous participez déjà au programme {{ getSolidoumeParam('name') }} à hauteur de <b>{{ participation.amount }} &euro;</b>.</p>
<a class='btn btn-primary' href='{{ path('solidoume_adherer') }}'>{{ "Modifier votre participation"|trans }}</a>
{% endif %}
{# {% set form = getSolidoumeForm() %}
{% if form != null %}
<p>{{ getSolidoumeParam('description')|raw }}</p>
{{form_start(form)}}
{{ form_row(form.user) }}
{{ form_row(form.adherent) }}
<div class='mb-4'>{{ form_row(form.amount) }}</div>
{{ form_row(form.isRecurrent) }}
{{ form_row(form.paiementDate) }}
{{ form_row(form.isDonation) }}
{{ form_row(form.save) }}
{{form_end(form)}}
{% endif %} #}
{% endblock blockcontent %}
\ No newline at end of file
{% extends '@SonataAdmin/CRUD/base_list.html.twig' %}
{% block list_table %}
<div class="col-xs-10 col-md-10 card p-4 m-4">
{{form_start(form)}}
{{ form_row(form.name) }}
{{ form_row(form.description) }}
{{ form_row(form.minimum) }}
{{ form_row(form.maximum) }}
{{ form_row(form.commission) }}
{{ form_row(form.executionDate) }}
{{ form_row(form.reminderDays) }}
{{ form_row(form.reminderEmail) }}
{{ form_row(form.confirmEmail) }}
{{ form_row(form.save) }}
{{form_end(form)}}
</div>
{% endblock list_table %}
{% block list_filters_actions %}
{% endblock list_filters_actions %}
{% block list_filters %}
{% endblock list_filters %}
{% block title %}
{% endblock %}
{% block navbar_title %}
{% endblock %}
\ No newline at end of file
......@@ -15,3 +15,6 @@
{% include '@kohinos/block/operations.html.twig' %}
{% include '@kohinos/block/cotisations.html.twig' %}
{% include '@kohinos/adherent/block/infos.html.twig' %}
{% if KOH_USE_SOLIDOUME|default('false') == 'true' %}
{% include '@kohinos/adherent/block/solidoume.html.twig' %}
{% endif %}
\ No newline at end of file
......@@ -7,20 +7,22 @@
{% include '@kohinos/block/operations.html.twig' %}
{% 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'))} %}
{% else %}
{% elseif getCurrentPrestataire().solidoume == false %}
{% include '@kohinos/block/cotisations.html.twig' %}
{% set groupeprestataires = getAllGroupePrestataires() %}
{% if groupeprestataires|length > 0 %}
{% include '@kohinos/groupepresta/block/inscription.html.twig' %}
{% endif %}
{% endif %}
{% if isPayzenEnabled() %}
{% if getCurrentPrestataire().solidoume == false %}
{% if isPayzenEnabled() %}
{% include '@kohinos/presta/block/achat_monnaie.html.twig' %}
{% else %}
{% else %}
{% include '@kohinos/presta/block/demande_achat_monnaie.html.twig' %}
{% endif %}
{% endif %}
{% include '@kohinos/presta/block/transaction_presta.html.twig' %}
{% include '@kohinos/presta/block/transaction_adherent.html.twig' %}
{% if getCurrentPrestataire().mlc == false and getCurrentPrestataire().emlcAccount.balance > 0 %}
{% if getCurrentPrestataire().mlc == false and getCurrentPrestataire().solidoume == false and getCurrentPrestataire().emlcAccount.balance > 0 %}
{% include '@kohinos/presta/block/reconversion.html.twig' %}
{% endif %}
\ No newline at end of file
......@@ -31,10 +31,18 @@
<td>
<span style='cursor:pointer;' data-toggle="collapse" data-target=".operationInfo{{loop.index}}">
{% if not isCurrentAccountable(app.user, operation.flux.expediteur) %}
{% if operation.flux.expediteur is instanceof("App\\Entity\\Prestataire") and operation.flux.expediteur.isSolidoume == true %}
{{ getSolidoumeParam('name')|default('Sécurité Sociale Alimentaire') }}
{% else %}
{{ operation.flux.expediteur }}
{% endif %}
{% elseif not isCurrentAccountable(app.user, operation.flux.destinataire) %}
{% if operation.flux.destinataire is instanceof("App\\Entity\\Prestataire") and operation.flux.destinataire.isSolidoume == true %}
{{ getSolidoumeParam('name')|default('Sécurité Sociale Alimentaire') }}
{% else %}
{{ operation.flux.destinataire }}
{% endif %}
{% endif %}
<i class="fa fa-caret-down ml-2" aria-hidden="true"></i>
</span>
<div class='collapse operationInfo{{loop.index}}'>
......
{% extends '@kohinos/bundles/SonataAdminBundle/CRUD/list.html.twig' %}
{% block list_header %}
{{ parent() }}
<div class="box-footer">
<div class="form-inline clearfix">
{% set datas = getSolidoumeDatas() %}
{% if null != datas %}
{{ datas.countParticipants }} participant(s), {{ datas.countDonateurs }} donateur(s) pour un total de {{ datas.total }}€ ce qui ferait <b>{{ datas.totalByParticipantWithCommission }}€ par participant(s)</b> (commission de {{datas.commission}}%)
{% endif %}
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -8,11 +8,16 @@
{% if app.user and is_granted('ROLE_ADHERENT') %}
<h4>{{ 'Solde de eMLC'|trans }} : <b>{{ app.user.adherent.emlcAccount.balance }}</b></h4>
<h5>{{ 'Montant minimum de la cotisation'|trans }} : <b>{{ KOH_COTISATION_ADHERENT|default('') }}</b></h5>
{% if KOH_ADHESION_TEXT|default('') != '' %}
<br/><p>
{{ KOH_ADHESION_TEXT }}
</p>
{% endif %}
{% elseif app.user and is_granted('ROLE_PRESTATAIRE') %}
<h4>{{ 'Solde de eMLC'|trans }} : <b>{{ getCurrentPrestataire().emlcAccount.balance }}</b></h4>
<h5>{{ 'Montant minimum de la cotisation'|trans }} : <b>{{ KOH_COTISATION_PRESTATAIRE|default('') }}</b></h5>
{% endif %}
</h5>
<br/>
{{ form_start(form) }}
{{ form_row(form.operateur) }}
{{ form_row(form.role) }}
......
{% extends '@kohinos/email/email_layout.html.twig' %}
{% set title %}{% spaceless %}
{{ subject }}
{% endspaceless %}
{% endset %}
{% block content %}
{{ getSolidoumeParam('confirmEmail')|replace({
'%name%' : getSolidoumeParam('name'),
'%adherent_name%' : item.adherent.name,
'%amount%' : item.amount,
'%paiementDate%' : item.paiementDate,
'%executionDate%' : getSolidoumeParam('executionDate')
})|raw }}
{% endblock %}
\ No newline at end of file
{% extends '@kohinos/email/email_layout.html.twig' %}
{% set title %}{% spaceless %}
{{ subject }}
{% endspaceless %}
{% endset %}
{% block content %}
{{ getSolidoumeParam('reminderEmail')|replace({
'%name%' : getSolidoumeParam('name'),
'%adherent_name%' : item.adherent.name,
'%amount%' : item.amount,
'%paiementDate%' : item.paiementDate,
'%executionDate%' : getSolidoumeParam('executionDate')
})|raw }}
{% endblock %}
\ No newline at end of file
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
<div class='container homepage'>
{% include '@kohinos/block/breadcrumb.html.twig' with {'label' : param.name } %}
<div class="card mx-auto mt-2">
<div class="card-header">{{ param.name }}</div>
<div class="card-body">
<p>{{ param.description|raw }}</p><br/><br/>
<div class="mx-auto text-center" style="max-width:800px;">
{{form_start(form)}}
<input type="hidden" name="referer" value="{{app.request.headers.get('referer')}}"/>
{{ form_row(form.user) }}
{{ form_row(form.adherent) }}
{{ form_row(form.amount) }}
{{ form_row(form.isRecurrent) }}
{{ form_row(form.paiementDate) }}
{{ form_row(form.isDonation) }}
<div class='row mt-4'>
{% if getSolidoumeParticipation() == null %}
<div class='col-12'>
{{ form_widget(form.save) }}
</div>
{% else %}
<div class='col-6'>
{{ form_widget(form.save) }}
</div>
<div class='col-6'>
<a class='btn btn-danger' href='{{ path('solidoume_unsubscribe') }}'>Se désinscrire</a>
</div>
{% endif %}
</div>
{{form_end(form)}}
</div>
</div>
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -44,7 +44,7 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase
{
$client = static::createClient([]);
$session = self::$container->get('session');
$person = self::$container->get('doctrine.orm.entity_manager')->getRepository(User::class)->findOneByUsername('julien.jorry@gmail.com');
$person = self::$container->get('doctrine.orm.entity_manager')->getRepository(User::class)->findOneByEmail('julien.jorry@gmail.com');
$token = new UsernamePasswordToken($person, null, 'main', $person->getRoles());
$session->set('_security_mlc_context', serialize($token));
......
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