Commit e26663ec by Damien Moulard

merge conflicts

parents c203aeb3 3a401fc8
......@@ -405,3 +405,27 @@ form[name="formEncaissement"] label {
text-align: right;
font-style: italic;
}
.presta-conventionnement-applied-text {
margin-top: -0.75rem !important;
margin-bottom: 1rem;
font-size: 100%;
}
.montant-panier-error {
margin-top: -0.75rem !important;
margin-bottom: 1rem;
font-size: 100%;
color: #ff4136;
}
.input-error {
border-color: #ff4136 !important;
}
.input-error:focus {
box-shadow: 0 0 0 0.2rem #ff403667 !important;
}
.text-error {
color: #ff4136;
}
\ No newline at end of file
......@@ -494,6 +494,88 @@ $(function() {
// Set payment summary on validation page
let recap = `${$("#formEncaissement_adherent option:selected").text()}, montant : ${$("#formEncaissement_montant").val()} ${KOH_MLC_NAME_SMALL}`;
$(".payment-recap").text(recap);
// If conventionnement mode is active
if ($(this).find("#formEncaissement_montantPanier").length > 0) {
$("#formEncaissement_montant").prop("disabled",true);
let maxAmount = 0;
function disableMontant() {
$("#formEncaissement_montant").val("");
$("#formEncaissement_montant").prop("disabled", true);
$(".presta-conventionnement-applied-text").hide();
}
/**
* If basket amount is set and valid, enable emlc amount field & hide errors if needed.
* Else display errors.
*/
function checkMontantPanier() {
let input = $("#formEncaissement_montantPanier");
let val = input.val();
if (val === '') {
// reset
$(".montant-panier-error").hide();
input.removeClass('input-error');
disableMontant();
} else {
let basketAmount = parseFloat(val);
if (isNaN(basketAmount)) {
// display error and reset the rest
$(".montant-panier-error").show();
input.addClass('input-error');
disableMontant();
return;
}
$(".montant-panier-error").hide();
input.removeClass('input-error');
// no error: enable amount field, set max emlc amount and display helper text
$("#formEncaissement_montant").prop("disabled", false);
$(".presta-conventionnement-applied-text").show();
maxAmount = parseFloat(formEncaissementConventionnement) * basketAmount;
$(".encaissment-emlc-max-amount").text(maxAmount);
}
checkMaxAmount();
}
checkMontantPanier();
$("#formEncaissement_montantPanier").on("input", checkMontantPanier);
/**
* Check that emlc amount is valid depending on basket amount
*/
function checkMaxAmount() {
function reset() {
$("#formEncaissement_montant").removeClass('input-error');
$(".encaissment-emlc-max-amount-container").removeClass("text-error");
}
let val = $("#formEncaissement_montant").val();
if (val === '') {
reset();
} else {
let amount = parseFloat(val);
if (!isNaN(amount) && amount > maxAmount) {
$(this).addClass('input-error');
$(".encaissment-emlc-max-amount-container").addClass("text-error");
} else if (!isNaN(amount) && amount <= maxAmount) {
reset();
}
}
}
checkMaxAmount();
$("#formEncaissement_montant").on("input", checkMaxAmount);
}
}
/**
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -3,10 +3,10 @@
"app": {
"js": [
"/build/runtime.6ad5c9da.js",
"/build/app.435420b4.js"
"/build/app.27091a64.js"
],
"css": [
"/build/app.7133af83.css"
"/build/app.5cdc32e6.css"
]
},
"admin": {
......
{
"build/app.css": "/build/app.7133af83.css",
"build/app.js": "/build/app.435420b4.js",
"build/app.css": "/build/app.5cdc32e6.css",
"build/app.js": "/build/app.27091a64.js",
"build/admin.css": "/build/admin.4de55830.css",
"build/admin.js": "/build/admin.86a2d986.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.4de55830.css'},{'revision':null,'url':'/build/admin.86a2d986.js'},{'revision':'ad79405d542b397996a3079203114b42','url':'/build/admin.86a2d986.js.LICENSE.txt'},{'revision':null,'url':'/build/app.435420b4.js'},{'revision':'4ff7361e3c3e359ae88c5fae5738746d','url':'/build/app.435420b4.js.LICENSE.txt'},{'revision':null,'url':'/build/app.7133af83.css'},{'revision':null,'url':'/build/fonts/fa-brands-400.3dc44d22.woff2'},{'revision':null,'url':'/build/fonts/fa-regular-400.3dc6ca01.woff2'},{'revision':null,'url':'/build/fonts/fa-solid-900.496d5fc1.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-300.d2c7d5c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600.17c0392c.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600italic.cc34c6e7.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-700.ed37bc60.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-900.476756cd.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-italic.a07cb9c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-regular.f74389bd.woff2'},{'revision':null,'url':'/build/images/fa-brands-400.05d20183.svg'},{'revision':null,'url':'/build/images/fa-regular-400.9a0810d6.svg'},{'revision':null,'url':'/build/images/fa-solid-900.a838c42a.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-300.4e7fe004.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600.cf2758ae.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600italic.7249d863.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-700.3e4b9e19.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-900.060d8c51.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-italic.08dc9b1c.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-regular.3bb9538c.svg'},{'revision':null,'url':'/build/runtime.6ad5c9da.js'}]),K(T),self.addEventListener("fetch",(function(e){e.respondWith(caches.match(e.request).then((function(t){return t||fetch(e.request)})).catch((function(){return caches.match("/offline.html")})))}))})()})();
\ No newline at end of file
(()=>{"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.4de55830.css'},{'revision':null,'url':'/build/admin.86a2d986.js'},{'revision':'ad79405d542b397996a3079203114b42','url':'/build/admin.86a2d986.js.LICENSE.txt'},{'revision':null,'url':'/build/app.27091a64.js'},{'revision':'4ff7361e3c3e359ae88c5fae5738746d','url':'/build/app.27091a64.js.LICENSE.txt'},{'revision':null,'url':'/build/app.5cdc32e6.css'},{'revision':null,'url':'/build/fonts/fa-brands-400.3dc44d22.woff2'},{'revision':null,'url':'/build/fonts/fa-regular-400.3dc6ca01.woff2'},{'revision':null,'url':'/build/fonts/fa-solid-900.496d5fc1.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-300.d2c7d5c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600.17c0392c.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-600italic.cc34c6e7.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-700.ed37bc60.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-900.476756cd.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-italic.a07cb9c5.woff2'},{'revision':null,'url':'/build/fonts/source-sans-pro-v14-latin-regular.f74389bd.woff2'},{'revision':null,'url':'/build/images/fa-brands-400.05d20183.svg'},{'revision':null,'url':'/build/images/fa-regular-400.9a0810d6.svg'},{'revision':null,'url':'/build/images/fa-solid-900.a838c42a.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-300.4e7fe004.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600.cf2758ae.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-600italic.7249d863.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-700.3e4b9e19.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-900.060d8c51.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-italic.08dc9b1c.svg'},{'revision':null,'url':'/build/images/source-sans-pro-v14-latin-regular.3bb9538c.svg'},{'revision':null,'url':'/build/runtime.6ad5c9da.js'}]),K(T),self.addEventListener("fetch",(function(e){e.respondWith(caches.match(e.request).then((function(t){return t||fetch(e.request)})).catch((function(){return caches.match("/offline.html")})))}))})()})();
\ No newline at end of file
......@@ -8,6 +8,7 @@ use App\Entity\CotisationPrestataire;
use App\Entity\GlobalParameter;
use App\Entity\Payment;
use App\Entity\Prestataire;
use App\Entity\TransactionPrestataireAdherent;
use App\Entity\User;
use App\Entity\TransactionAdherentPrestataire;
use App\Enum\MoyenEnum;
......@@ -242,15 +243,105 @@ class UserController extends AbstractController
return $this->redirectToRoute('index');
}
/**
* @param Request $request
* @param TransactionAdherentPrestataire $transactionAdherentPrestataire
* @return void
* @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"})
* @Route("/cancel-transaction-adherent-prestataire/{id}", name="cancel_transaction_adherent_prestataire")
*/
public function cancelTransactionAdherentPrestataireAction(Request $request, TransactionAdherentPrestataire $transactionAdherentPrestataire)
{
$adherent = $transactionAdherentPrestataire->getExpediteur();
$presta = $transactionAdherentPrestataire->getDestinataire();
//Make sure current user is destinataire of transaction
if(!in_array($this->getUser(),$presta->getUsers()->toArray())) {
$this->addFlash(
'error',
"Vous n'êtes pas autorisé à annuler cette transaction."
);
return $this->redirectToRoute('index');
}
//Make sure flux has not been cancelled already
if($transactionAdherentPrestataire->getCancellerFlux()) {
$this->addFlash(
'error',
"Cette transaction a déjà été annulée."
);
return $this->redirectToRoute('index');
}
// Check prestataire has enough funds
$balance = $presta->getEmlcAccount()->getBalance();
//I think this is unlikely in real situation but we need to make sure prestataire has enough founds
$montant = $transactionAdherentPrestataire->getMontant();
if ($balance < $montant) {
$this->addFlash(
'error',
'Fonds insuffisants pour annuler cette transaction.'
);
return $this->redirectToRoute('index');
}
//Create new transaction in opposite direction
$flux = new TransactionPrestataireAdherent();
$flux->setExpediteur($presta);
$flux->setDestinataire($adherent);
$flux->setOperateur($this->security->getUser());
$flux->setMontant($montant);
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$now = (new \Datetime('now'))->format('d/m/Y H:i:s');
$flux->setReference(
'Remboursement en Monnaie Solidaire du ' . $now . ', annule ' . $transactionAdherentPrestataire->getReference()
);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
//Mark original transaction as cancelled
$transactionAdherentPrestataire->setCancellerFlux($flux);
$this->em->flush();
$this->addFlash(
'success',
'La transaction a bien été annulée.'
);
return $this->redirectToRoute('index');
}
/**
* Payment terminal page.
*
* @Route("/encaissement", name="encaissement")
* @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"})
*/
public function encaissementAction(Request $request)
{
// If conventionnement process is activated, prevent access to payment terminal if prestataire conventionnement is not set
$conventionnementMode = $this->getParameter('presta_self_init_and_eval');
if ($conventionnementMode == true) {
$presta = $this->session->get('_prestagere');
$presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId());
$conventionnement = $presta->getConventionnement();
if ($conventionnement == null || $conventionnement == 0) {
$this->addFlash(
'error',
$this->translator->trans("Impossible d'accéder au terminal de paiement tant que votre conventionnement n'a pas été établi par un·e gestionnaire.")
);
return $this->redirectToRoute('index');
}
}
$form = $this->createForm(EncaissementFormType::class, null, []);
$form->handleRequest($request);
$validation = false;
$validation = false; // validation = user enters its code
$insufficientBalance = null; // if not null, will indicate for the template an insufficient founds situation
if ($form->isSubmitted()) {
......@@ -259,34 +350,55 @@ class UserController extends AbstractController
if ($form->isValid()) {
$adherent = $data["adherent"];
$adherent_code = $adherent->getPaymentCode();
$input_code = $data["payment_code"];
// if adherent account isn't enabled
if (!$adherent->getUser()->isEnabled()) {
$this->addFlash(
'error',
$this->translator->trans('Le compte de l\'habitant·e est désactivé.')
);
// Perform first step checks
if (empty($input_code)) {
// if adherent account isn't enabled
if (!$adherent->getUser()->isEnabled()) {
$this->addFlash(
'error',
$this->translator->trans('Le compte de l\'habitant·e est désactivé.')
);
goto end;
}
goto end;
}
// if adherent doesn't have a validation code
if (is_null($adherent_code)) {
$this->addFlash(
'error',
$this->translator->trans('L\'habitant·e n\'a pas encore défini un code de validation de paiement sur son espace personnel, il·elle ne peut pas payer en Monnaie Solidaire pour l\'instant.')
);
// if adherent doesn't have a validation code
if (is_null($adherent_code)) {
$this->addFlash(
'error',
$this->translator->trans('L\'habitant·e n\'a pas encore défini un code de validation de paiement sur son espace personnel, il·elle ne peut pas payer en Monnaie Solidaire pour l\'instant.')
);
goto end;
}
goto end;
}
$input_code = $data["payment_code"];
$validation = true; // When the form is submitted & valid, and the user account checks passed, we enter the validation process
// If entered amount is above maximum amount, depending on prestataire conventionnement
if ($conventionnementMode == true) {
$transaction_amount = floatval($data["montant"]);
$montantPanier = floatval($form['montantPanier']->getData());
$maxAmount = $montantPanier * floatval($conventionnement);
if ($transaction_amount > $maxAmount) {
$this->addFlash(
'error',
$this->translator->trans('Montant maximal en MonA dépassé. Veuillez modifier le montant en MonA.')
);
goto end;
}
}
// When the form is submitted & valid, and the user account checks passed, we enter the validation process
$validation = true;
if (empty($input_code)) {
// First step validated (set user & amount) -> go to validation
goto end;
}
} else {
// We're in the validation process
$validation = true;
}
// Check validation code
// NOTE as we use password salt, the user must change his payment code if his password changes
......@@ -329,13 +441,15 @@ class UserController extends AbstractController
goto end;
}
// Save transaction
// No error at this point: save transaction
$flux = new TransactionAdherentPrestataire();
$flux->setExpediteur($adherent);
$presta = $this->session->get('_prestagere');
$presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId());
if (!isset($presta)) {
$presta = $this->session->get('_prestagere');
$presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId());
}
$flux->setDestinataire($presta);
$flux->setOperateur($this->security->getUser());
......@@ -349,7 +463,15 @@ class UserController extends AbstractController
$this->operationUtils->executeOperations($flux);
$this->em->flush();
return $this->redirectToRoute('encaissementSuccess');
$redirectParams = [];
if ($form->has('montantPanier')) {
$transaction_amount = floatval($data["montant"]);
$montantPanier = floatval($form['montantPanier']->getData());
$redirectParams['remainingAmount'] = $montantPanier - $transaction_amount;
}
return $this->redirectToRoute('encaissementSuccess', $redirectParams);
} else {
$this->addFlash(
'error',
......@@ -359,11 +481,17 @@ class UserController extends AbstractController
}
end:
return $this->render('@kohinos/tav/encaissement_page.html.twig', [
$templateData = [
'form' => $form->createView(),
'validation' => $validation,
'insufficientBalance' => $insufficientBalance
]);
];
if ($conventionnementMode == true) {
$templateData['conventionnement'] = $conventionnement;
}
return $this->render('@kohinos/tav/encaissement_page.html.twig', $templateData);
}
/**
......@@ -372,6 +500,13 @@ class UserController extends AbstractController
*/
public function encaissementSuccessAction(Request $request)
{
return $this->render('@kohinos/tav/payment_done_page.html.twig', []);
$templateData = [];
$remainingAmount = $request->query->get('remainingAmount');
if ($remainingAmount != '') {
$templateData['remainingAmount'] = $remainingAmount;
}
return $this->render('@kohinos/tav/payment_done_page.html.twig', $templateData);
}
}
......@@ -29,6 +29,15 @@ class TransactionAdherentPrestataire extends Transaction
protected $destinataire;
/**
* If the transaction is cancelled, cancellerFlux is the
* opposite TransactionPrestataireAdherent flux.
*
* @ORM\ManyToOne(targetEntity="Flux")
* @ORM\JoinColumn(name="cancellerflux_id", referencedColumnName="id", nullable=true)
*/
protected $cancellerFlux;
/**
* @return string
*/
public function getType(): string
......@@ -43,4 +52,7 @@ class TransactionAdherentPrestataire extends Transaction
'destinataires' => $this->getDestinataire()->getUsers()->toArray(),
];
}
public function setCancellerFlux($var) {$this->cancellerFlux = $var;}
public function getCancellerFlux() {return $this->cancellerFlux;}
}
......@@ -10,23 +10,29 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\DependencyInjection\ContainerInterface;
use App\Entity\Adherent;
use App\Entity\GlobalParameter;
class EncaissementFormType extends AbstractType
{
protected $em;
protected $security;
protected $container;
public function __construct(
EntityManagerInterface $em,
Security $security
Security $security,
ContainerInterface $container
) {
$this->em = $em;
$this->security = $security;
$this->container = $container;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$mlcName = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL);
$builder
->add('adherent', EntityType::class, [
'class' => Adherent::class,
......@@ -40,7 +46,7 @@ class EncaissementFormType extends AbstractType
'choice_label' => 'name'
])
->add('montant', NumberType::class, [
'label' => 'Montant : ',
'label' => "Montant à payer en {$mlcName} : ",
'required' => true,
'attr' => ['autocomplete' => 'off']
])
......@@ -51,6 +57,20 @@ class EncaissementFormType extends AbstractType
])
->add('save', SubmitType::class, ['label' => 'Valider'])
;
if ($this->container->getParameter('presta_self_init_and_eval')) {
$builder
->add('montantPanier', NumberType::class, [
'label' => 'Montant total du panier : ',
'required' => true,
'attr' => ['autocomplete' => 'off'],
'mapped' => false,
'help' => ''
])
;
}
}
public function getBlockPrefix()
......
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240312135007 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE flux ADD cancellerflux_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE flux ADD CONSTRAINT FK_7252313ABB04641D FOREIGN KEY (cancellerflux_id) REFERENCES flux (id)');
$this->addSql('CREATE INDEX IDX_7252313ABB04641D ON flux (cancellerflux_id)');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE flux DROP FOREIGN KEY FK_7252313ABB04641D');
$this->addSql('DROP INDEX IDX_7252313ABB04641D ON flux');
$this->addSql('ALTER TABLE flux DROP cancellerflux_id');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
......@@ -8,7 +8,7 @@
{% include '@kohinos/presta/block/infos.html.twig' %}
{% endif %}
{# {% include '@kohinos/block/transactions.html.twig' %} #}
{% include '@kohinos/block/operations.html.twig' %}
{% include '@kohinos/block/operations.html.twig' with {'display_cancel_transaction_btn' : tav_env} %}
{% if not tav_env %}
{% if getCurrentPrestataire().mlc == true %}
{% include '@kohinos/block/operations.html.twig' with {'title' : 'Compte de fonctionnement €', 'operations': getLastOperations(app.request, app.user, constant('App\\Enum\\CurrencyEnum::CURRENCY_EURO'))} %}
......
......@@ -17,6 +17,10 @@
<th scope="col">Type</th>
<th scope="col">Operation</th>
<th scope="col">Montant</th>
{# Display column to store btn to cancel transaction adherent prestataire #}
{% if display_cancel_transaction_btn is defined and display_cancel_transaction_btn %}
<th scope="col"></th>
{% endif %}
</tr>
</thead>
<tbody>
......@@ -55,7 +59,28 @@
Moyen : {{ operation.flux.moyen|trans }}
</div>
</td>
<td class='text-right'><span class='font-weight-bold {{ (operation.montant < 0)? '':'text-primary' }}'>{{ (operation.montant < 0)? '- ':'+ ' }} {{ operation.montant|abs|number_format(2) }} {{ getCurrencyName(operation.currency) }}</span></td>
<td><span class='font-weight-bold {{ (operation.montant < 0)? '':'text-primary' }}'>{{ (operation.montant < 0)? '- ':'+ ' }} {{ operation.montant|abs|number_format(2) }} {{ getCurrencyName(operation.currency) }}</span></td>
{# Display btn to cancel transaction adherent prestataire #}
{% if display_cancel_transaction_btn is defined and display_cancel_transaction_btn
and operation.flux.type is same as(constant('App\\Entity\\Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE'))
and app.user in operation.flux.destinataire.users %}
<td class='text-right'>
{# If transaction has been cancelled already #}
{% if operation.flux.cancellerFlux %}
<button class="btn btn-sm" title="Cette transaction a déjà été annulée." disabled>
<i class="fas fa-undo"></i>
</button>
{% else %}
<a href="{{ path('cancel_transaction_adherent_prestataire', {'id': operation.flux.id}) }}"
type="button"
class="btn btn-sm btn-primary"
title="Annuler cette transaction."
onclick="return confirm('Êtes-vous sûr de vouloir annuler cette transaction et rembourser le client ? L\'opération est irréversible.')">
<i class="fas fa-undo"></i>
</a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
......
......@@ -14,7 +14,7 @@
<b>{{ app.session.get('_prestagere') }}</b>
</div>
<h2 class='text-center w-100 mt-4 mb-4'>{{ "Paiement en MonA"|trans }}</h2>
<h2 class='text-center w-100 mt-4 mb-4'>{{ "Paiement"|trans }}</h2>
{% else %}
<div class="mt-3 payment-page-header">
<a href='{{ path('encaissement') }}'>
......@@ -71,10 +71,27 @@
<div style="display:none;">
{{ form_row(form.adherent) }}
{{ form_row(form.montant) }}
{% if form.montantPanier is defined %}
{{ form_row(form.montantPanier) }}
{% endif %}
</div>
{% else %}
{{ form_row(form.adherent) }}
{% if form.montantPanier is defined %}
{{ form_row(form.montantPanier) }}
<div class="presta-conventionnement-applied-text form-text text-muted" style="display:none;">
Conventionnement du point de vente : {{ conventionnement * 100 }}% ;
<span class="encaissment-emlc-max-amount-container">
le montant maximum payable en MonA est de : <span class="encaissment-emlc-max-amount"></span> MonA
</span>
</div>
<div class="montant-panier-error form-text" style="display:none;">La valeur est invalide.</div>
{% endif %}
{{ form_row(form.montant) }}
<br/>
{% endif %}
<br/>
......@@ -86,6 +103,13 @@
var formEncaissementValidation = '{{ validation }}';
var KOH_MLC_NAME_SMALL = "{{ KOH_MLC_NAME_SMALL|default('') }}";
</script>
{% if conventionnement is defined %}
<script type="text/javascript">
var formEncaissementConventionnement = '{{ conventionnement }}';
</script>
{% endif %}
{% endblock %}
{% block footer %}{% endblock footer %}
\ No newline at end of file
......@@ -5,7 +5,9 @@
{% block content %}
<div class='container' style='max-width: 800px;'>
<div class="payment-done-container">
<h1 class="text-center w-100 mt-4 payment-done-title">Paiement réussi !</h1>
<h1 class="text-center w-100 mt-4 payment-done-title">
{{ 'Paiement réussi !'|trans }}
</h1>
<br/>
......@@ -20,6 +22,12 @@
<br/>
{% if remainingAmount is defined %}
<h5>{{ 'Reste à payer en euros :'|trans }} {{ remainingAmount }}</h5>
<br/>
{% endif %}
<a class='btn btn-xs btn-primary mt-4' href='{{ path('encaissement') }}'>
{{ 'Nouvel encaissement'|trans }}
</a>
......
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