Skip to content

Commit

Permalink
Notification des techniciens dans les inventaires de retour (#323)
Browse files Browse the repository at this point in the history
  • Loading branch information
polosson authored Jun 9, 2023
1 parent f2656cb commit b3b9781
Show file tree
Hide file tree
Showing 17 changed files with 338 additions and 37 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Ce projet adhère au principe du [Semantic Versioning](https://semver.org/spec/v
- Ajoute la possibilité de choisir un emplacement de rangement pour chaque matériel
au sein d'un parc, et affiche cette information dans les fiches de sorties et les
inventaires de retour (Premium #294).
- Dans les inventaires de retour des événements, un bouton permet d'envoyer une notification
par e-mail aux techniciens assignés à l'événement, tant que le matériel n'a pas été
complètement retourné, ou que l'inventaire n'est pas terminé (Premium #293).
- Dans le calendrier, un nouveau filtre permet de filtrer les événements par catégorie
du matériel qu'il contient (Premium #297).
- Affiche la durée des événements et réservations dans l'onglet "périodes de réservation"
Expand Down
9 changes: 7 additions & 2 deletions client/src/stores/api/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ const unarchive = async (id: Event['id']): Promise<Event> => (
);

const updateReturnInventory = async (id: Event['id'], inventory: EventReturnInventory): Promise<Event> => (
normalize((await requester.put(`/events/${id}/inventory`, inventory)).data)
normalize((await requester.put(`/events/${id}/return`, inventory)).data)
);

const finishReturnInventory = async (id: Event['id'], inventory: EventReturnInventory): Promise<Event> => (
normalize((await requester.put(`/events/${id}/inventory/finish`, inventory)).data)
normalize((await requester.put(`/events/${id}/return/finish`, inventory)).data)
);

const createInvoice = async (id: Event['id'], discountRate: number = 0): Promise<Invoice> => (
Expand Down Expand Up @@ -252,6 +252,10 @@ const attachDocument = async (id: Event['id'], file: File, options: RequestConfi
return (await requester.post(`/events/${id}/documents`, formData, options)).data;
};

const notifyReturn = async (id: Event['id']): Promise<{ sent: number }> => (
(await requester.post(`/events/${id}/return/notify`)).data
);

export default {
all,
one,
Expand All @@ -269,4 +273,5 @@ export default {
remove,
documents,
attachDocument,
notifyReturn,
};
4 changes: 2 additions & 2 deletions client/src/stores/api/reservations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,11 @@ const missingMaterials = async (id: Reservation['id']): Promise<MissingReservati
);

const updateReturnInventory = async (id: Reservation['id'], inventory: ReservationReturnInventory): Promise<Reservation> => (
(await requester.put(`/reservations/${id}/inventory`, inventory)).data
(await requester.put(`/reservations/${id}/return`, inventory)).data
);

const finishReturnInventory = async (id: Reservation['id'], inventory: ReservationReturnInventory): Promise<Reservation> => (
(await requester.put(`/reservations/${id}/inventory/finish`, inventory)).data
(await requester.put(`/reservations/${id}/return/finish`, inventory)).data
);

const archive = async (id: Reservation['id']): Promise<Reservation> => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@
}

&__reference {
flex: 0 0 200px;
flex: 1;
}

&__name {
flex: 1;
display: none;
color: globals.$text-soft-color;
}

&__park-location {
flex: 1;
display: none;
color: globals.$text-soft-color;
font-size: 0.85rem;
}
Expand Down Expand Up @@ -68,12 +70,6 @@
}
}

&--with-park-location {
#{$block}__name {
flex: 0 0 330px;
}
}

&--complete {
background: rgba(globals.$text-success-color, 0.25);

Expand Down Expand Up @@ -105,4 +101,26 @@
font-weight: 600;
}
}

@media (min-width: globals.$screen-tablet) {
&__reference {
flex: 0 0 200px;
}

&__name {
display: block;
}
}

@media (min-width: globals.$screen-desktop) {
&__park-location {
display: block;
}

&--with-park-location {
#{$block}__name {
flex: 0 0 330px;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import './index.scss';
import { defineComponent } from '@vue/composition-api';
import Fragment from '@/components/Fragment';
import IconMessage from '@/themes/default/components/IconMessage';
import Button from '@/themes/default/components/Button';

// @vue/component
export default {
name: 'EventReturnFooter',
const EventReturnFooter = defineComponent({
name: '',
props: {
isDone: Boolean,
isSaving: Boolean,
hasEnded: Boolean,
canNotify: Boolean,
isNotifying: Boolean,
},
emits: ['save', 'terminate'],
emits: ['save', 'terminate', 'notify'],
methods: {
// ------------------------------------------------------
// -
Expand All @@ -26,15 +29,22 @@ export default {
handleClickTerminate() {
this.$emit('terminate');
},

handleClickNotify() {
this.$emit('notify');
},
},
render() {
const {
$t: __,
isDone,
isSaving,
hasEnded,
canNotify,
isNotifying,
handleClickSave,
handleClickTerminate,
handleClickNotify,
} = this;

return (
Expand Down Expand Up @@ -68,6 +78,17 @@ export default {
{isSaving ? __('saving') : __('terminate-inventory')}
</Button>
)}
{canNotify && (
<Button
class="EventReturnFooter__notify"
onClick={handleClickNotify}
tooltip={__('page.event-return.notify-technicians-tooltip')}
icon="bell"
loading={isNotifying}
>
{__('page.event-return.notify-technicians')}
</Button>
)}
{!hasEnded && (
<div class="EventReturnFooter__warning">
<IconMessage
Expand All @@ -81,4 +102,6 @@ export default {
</div>
);
},
};
});

export default EventReturnFooter;
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@
color: globals.$text-success-color;
}

&__action {
margin-left: globals.$content-padding-small-vertical;
}

&__warning {
margin-top: globals.$content-padding-large-horizontal;
color: globals.$text-warning-color;
Expand Down
63 changes: 61 additions & 2 deletions client/src/themes/default/pages/EventReturn/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const EventReturn = defineComponent({
isLoading: false,
isFetched: false,
isSaving: false,
isSaved: false,
isNotifying: false,
displayGroup: DisplayGroup.CATEGORIES,
criticalError: null,
validationErrors: null,
Expand Down Expand Up @@ -68,6 +70,25 @@ const EventReturn = defineComponent({
}
return !!this.event.is_return_inventory_done;
},

canNotify() {
const { event, inventory, isSaved } = this;
if (!event) {
return false;
}

const isAllReturned = event.materials.every(({ id: materialId, pivot }) => {
const quantities = inventory.find(({ id }) => id === materialId);
return quantities ? quantities.actual === pivot.quantity : false;
});

return (
event?.is_return_inventory_started &&
event.technicians.length > 0 &&
!isAllReturned &&
isSaved
);
},
},
mounted() {
this.fetchData();
Expand All @@ -93,6 +114,7 @@ const EventReturn = defineComponent({
return;
}
this.$set(this.inventory, index, { id, ...quantities });
this.isSaved = false;
},

handleChangeDisplayGroup(group) {
Expand Down Expand Up @@ -122,6 +144,35 @@ const EventReturn = defineComponent({
await this.save(true);
},

async handleNotify() {
const { $t: __, event, isNotifying } = this;
if (isNotifying) {
return;
}

const count = event.technicians.length;
const isConfirmed = await confirm({
type: 'warning',
text: __('page.event-return.please-confirm-send-notification', { count }, count),
confirmButtonText: __('page.event-return.yes-send'),
});

if (!isConfirmed) {
return;
}

this.isNotifying = true;

try {
const { sent } = await apiEvents.notifyReturn(this.id);
this.$toasted.success(__('page.event-return.technicians-notified', { sent }, sent));
} catch {
this.$toasted.error(__('errors.unknown'));
} finally {
this.isNotifying = false;
}
},

// ------------------------------------------------------
// -
// - Méthodes internes
Expand All @@ -133,6 +184,7 @@ const EventReturn = defineComponent({
const event = await apiEvents.one(this.id);
this.setEvent(event);
this.isFetched = true;
this.isSaved = event.is_return_inventory_started;
} catch (error) {
if (!axios.isAxiosError(error)) {
this.criticalError = ERROR.UNKNOWN;
Expand All @@ -159,8 +211,9 @@ const EventReturn = defineComponent({
);

try {
const _event = await doRequest();
this.setEvent(_event);
const event = await doRequest();
this.setEvent(event);
this.isSaved = true;

this.validationErrors = null;
this.$toasted.success(__('page.event-return.saved'));
Expand Down Expand Up @@ -222,11 +275,14 @@ const EventReturn = defineComponent({
isSaving,
hasEnded,
isDone,
canNotify,
isNotifying,
displayGroup,
handleChangeDisplayGroup,
handleChangeInventory,
handleSave,
handleTerminate,
handleNotify,
} = this;

if (criticalError || !isFetched) {
Expand Down Expand Up @@ -267,6 +323,9 @@ const EventReturn = defineComponent({
hasEnded={hasEnded}
onSave={handleSave}
onTerminate={handleTerminate}
canNotify={canNotify}
onNotify={handleNotify}
isNotifying={isNotifying}
/>
</Fragment>
)}
Expand Down
12 changes: 12 additions & 0 deletions client/src/themes/default/pages/EventReturn/translations/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ all-material-returned:
some-material-is-missing: Some materials did not return from this event!
some-material-came-back-broken: Some materials came back broken.

notify-technicians: Notify technicians
notify-technicians-tooltip: |-
Send an e-mail to technicians assigned to
the event with a list of missing materials.
please-confirm-send-notification:
- Do you really want to send an e-mail notification to the technician?
- Do you really want to send an e-mail notification to {count} technician(s)?
yes-send: Yes, send
technicians-notified:
- "The technician has been notified by e-mail."
- "{sent} technicians have been notified by e-mail."

confirm-terminate-title: Do you really want to terminate this return inventory?
confirm-terminate-text: Please note that it will no longer be possible to modify it.
confirm-terminate-text-with-broken: >-
Expand Down
12 changes: 12 additions & 0 deletions client/src/themes/default/pages/EventReturn/translations/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ all-material-returned:
some-material-is-missing: "Du matériel n'est pas revenu de cet événement\_!"
some-material-came-back-broken: Du matériel est revenu en panne.

notify-technicians: Notifier les techniciens
notify-technicians-tooltip: |-
Envoyer un e-mail aux techniciens assignés à
l'événement avec la liste du matériel manquant.
please-confirm-send-notification:
- "Voulez-vous vraiment envoyer une notification par e-mail au technicien\_?"
- "Voulez-vous vraiment envoyer une notification par e-mail aux {count} techniciens\_?"
yes-send: Oui, envoyer
technicians-notified:
- "Le technicien a bien été notifié par e-mail."
- "{sent} techniciens ont bien été notifiés par e-mail."

confirm-terminate-title: "Voulez-vous vraiment terminer cet inventaire de retour\_?"
confirm-terminate-text: Veuillez noter qu'il ne sera plus possible de le modifier.
confirm-terminate-text-with-broken:
Expand Down
2 changes: 2 additions & 0 deletions client/src/themes/default/style/vendors/_tooltip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
border-radius: 3px;
background: $bg-color-tooltip;
color: $text-soft-color;
text-align: center;
white-space: pre-line;
}
}
1 change: 1 addition & 0 deletions server/src/App/Config/Acl.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class Acl
'restore',
'updateReturnInventory',
'finishReturnInventory',
'notifyAboutReturnInventory',
'delete',
'createInvoice',
'createEstimate',
Expand Down
Loading

0 comments on commit b3b9781

Please sign in to comment.