Bug 32495: Mis-matched required fields in UI and api
[koha-ffzg.git] / koha-tmpl / intranet-tmpl / prog / js / vue / components / ERM / AgreementsFormAdd.vue
1 <template>
2     <div v-if="!initialized">{{ $__("Loading") }}</div>
3     <div v-else id="agreements_add">
4         <h2 v-if="agreement.agreement_id">
5             {{ $__("Edit agreement #%s").format(agreement.agreement_id) }}
6         </h2>
7         <h2 v-else>{{ $__("New agreement") }}</h2>
8         <div>
9             <form @submit="onSubmit($event)">
10                 <div class="page-section">
11                     <fieldset class="rows">
12                         <ol>
13                             <li>
14                                 <label class="required" for="agreement_name"
15                                     >{{ $__("Agreement name") }}:</label
16                                 >
17                                 <input
18                                     id="agreement_name"
19                                     v-model="agreement.name"
20                                     :placeholder="$__('Agreement name')"
21                                     required
22                                 />
23                                 <span class="required">{{
24                                     $__("Required")
25                                 }}</span>
26                             </li>
27                             <li>
28                                 <label for="agreement_vendor_id"
29                                     >{{ $__("Vendor") }}:</label
30                                 >
31                                 <v-select
32                                     id="agreement_vendor_id"
33                                     v-model="agreement.vendor_id"
34                                     label="name"
35                                     :reduce="vendor => vendor.id"
36                                     :options="vendors"
37                                 />
38                             </li>
39                             <li>
40                                 <label for="agreement_description"
41                                     >{{ $__("Description") }}:
42                                 </label>
43                                 <textarea
44                                     id="agreement_description"
45                                     v-model="agreement.description"
46                                     :placeholder="$__('Description')"
47                                     rows="10"
48                                     cols="50"
49                                     required
50                                 />
51                             </li>
52                             <li>
53                                 <label for="agreement_status"
54                                     >{{ $__("Status") }}:</label
55                                 >
56                                 <v-select
57                                     id="agreement_status"
58                                     v-model="agreement.status"
59                                     label="lib"
60                                     :reduce="av => av.authorised_value"
61                                     :options="av_agreement_statuses"
62                                     @option:selected="onStatusChanged"
63                                     :required="!agreement.status"
64                                 >
65                                     <template #search="{ attributes, events }">
66                                         <input
67                                             :required="!agreement.status"
68                                             class="vs__search"
69                                             v-bind="attributes"
70                                             v-on="events"
71                                         />
72                                     </template>
73                                 </v-select>
74                                 <span class="required">{{
75                                     $__("Required")
76                                 }}</span>
77                             </li>
78                             <li>
79                                 <label for="agreement_closure_reason"
80                                     >{{ $__("Closure reason") }}:</label
81                                 >
82                                 <v-select
83                                     id="agreement_closure_reason"
84                                     v-model="agreement.closure_reason"
85                                     label="lib"
86                                     :reduce="av => av.authorised_value"
87                                     :options="av_agreement_closure_reasons"
88                                     :disabled="
89                                         agreement.status == 'closed'
90                                             ? false
91                                             : true
92                                     "
93                                 />
94                             </li>
95                             <li>
96                                 <label
97                                     for="agreement_is_perpetual"
98                                     class="radio"
99                                     >{{ $__("Is perpetual") }}:</label
100                                 >
101                                 <label for="agreement_is_perpetual_yes">
102                                     <input
103                                         type="radio"
104                                         name="is_perpetual"
105                                         id="agreement_is_perpetual_yes"
106                                         :value="true"
107                                         v-model="agreement.is_perpetual"
108                                     />
109                                     Yes
110                                 </label>
111                                 <label for="agreement_is_perpetual_no">
112                                     <input
113                                         type="radio"
114                                         name="is_perpetual"
115                                         id="agreement_is_perpetual_no"
116                                         :value="false"
117                                         v-model="agreement.is_perpetual"
118                                     />
119                                     No
120                                 </label>
121                             </li>
122                             <li>
123                                 <label for="agreement_renewal_priority"
124                                     >{{ $__("Renewal priority") }}:</label
125                                 >
126                                 <v-select
127                                     id="agreement_renewal_priority"
128                                     v-model="agreement.renewal_priority"
129                                     label="lib"
130                                     :reduce="av => av.authorised_value"
131                                     :options="av_agreement_renewal_priorities"
132                                 />
133                             </li>
134                             <li>
135                                 <label for="agreement_license_info"
136                                     >{{ $__("License info") }}:
137                                 </label>
138                                 <textarea
139                                     id="agreement_license_info"
140                                     v-model="agreement.license_info"
141                                     placeholder="License info"
142                                 />
143                             </li>
144                         </ol>
145                     </fieldset>
146                 </div>
147                 <AgreementPeriods :periods="agreement.periods" />
148                 <UserRoles
149                     :user_type="$__('Agreement user')"
150                     :user_roles="agreement.user_roles"
151                     :av_user_roles="av_user_roles"
152                 />
153                 <AgreementLicenses
154                     :agreement_licenses="agreement.agreement_licenses"
155                     :av_agreement_license_statuses="
156                         av_agreement_license_statuses
157                     "
158                     :av_agreement_license_location="
159                         av_agreement_license_location
160                     "
161                 />
162                 <AgreementRelationships
163                     :agreement_id="agreement.agreement_id"
164                     :relationships="agreement.agreement_relationships"
165                     :av_agreement_relationships="av_agreement_relationships"
166                 />
167                 <Documents :documents="agreement.documents" />
168                 <fieldset class="action">
169                     <input type="submit" value="Submit" />
170                     <router-link
171                         to="/cgi-bin/koha/erm/agreements"
172                         role="button"
173                         class="cancel"
174                         >{{ $__("Cancel") }}</router-link
175                     >
176                 </fieldset>
177             </form>
178         </div>
179     </div>
180 </template>
181
182 <script>
183 import { inject } from "vue"
184 import AgreementPeriods from "./AgreementPeriods.vue"
185 import UserRoles from "./UserRoles.vue"
186 import AgreementLicenses from "./AgreementLicenses.vue"
187 import AgreementRelationships from "./AgreementRelationships.vue"
188 import Documents from "./Documents.vue"
189 import { setMessage, setError, setWarning } from "../../messages"
190 import { fetchAgreement, checkError } from "../../fetch"
191 import { storeToRefs } from "pinia"
192
193 export default {
194     setup() {
195         const vendorStore = inject("vendorStore")
196         const { vendors } = storeToRefs(vendorStore)
197
198         const AVStore = inject("AVStore")
199         const {
200             av_agreement_statuses,
201             av_agreement_closure_reasons,
202             av_agreement_renewal_priorities,
203             av_user_roles,
204             av_agreement_license_statuses,
205             av_agreement_license_location,
206             av_agreement_relationships,
207         } = storeToRefs(AVStore)
208
209         return {
210             vendors,
211             av_agreement_statuses,
212             av_agreement_closure_reasons,
213             av_agreement_renewal_priorities,
214             av_user_roles,
215             av_agreement_license_statuses,
216             av_agreement_license_location,
217             av_agreement_relationships,
218             max_allowed_packet,
219         }
220     },
221     data() {
222         return {
223             agreement: {
224                 agreement_id: null,
225                 name: "",
226                 vendor_id: null,
227                 description: "",
228                 status: "",
229                 closure_reason: "",
230                 is_perpetual: false,
231                 renewal_priority: "",
232                 license_info: "",
233                 periods: [],
234                 user_roles: [],
235                 agreement_licenses: [],
236                 agreement_relationships: [],
237                 documents: [],
238             },
239             initialized: false,
240         }
241     },
242     beforeRouteEnter(to, from, next) {
243         next(vm => {
244             if (to.params.agreement_id) {
245                 vm.agreement = vm.getAgreement(to.params.agreement_id)
246             } else {
247                 vm.initialized = true
248             }
249         })
250     },
251     methods: {
252         async getAgreement(agreement_id) {
253             const agreement = await fetchAgreement(agreement_id)
254             this.agreement = agreement
255             this.initialized = true
256         },
257         checkForm(agreement) {
258             let errors = []
259
260             let agreement_licenses = agreement.agreement_licenses
261             // Do not use al.license.name here! Its name is not the one linked with al.license_id
262             // At this point al.license is meaningless, form/template only modified al.license_id
263             const license_ids = agreement_licenses.map(al => al.license_id)
264             const duplicate_license_ids = license_ids.filter(
265                 (id, i) => license_ids.indexOf(id) !== i
266             )
267
268             if (duplicate_license_ids.length) {
269                 errors.push(this.$__("A license is used several times"))
270             }
271
272             const related_agreement_ids = agreement.agreement_relationships.map(
273                 rs => rs.related_agreement_id
274             )
275             const duplicate_related_agreement_ids =
276                 related_agreement_ids.filter(
277                     (id, i) => related_agreement_ids.indexOf(id) !== i
278                 )
279
280             if (duplicate_related_agreement_ids.length) {
281                 errors.push(
282                     this.$__(
283                         "An agreement is used as relationship several times"
284                     )
285                 )
286             }
287
288             if (
289                 agreement_licenses.filter(al => al.status == "controlling")
290                     .length > 1
291             ) {
292                 errors.push(this.$__("Only one controlling license is allowed"))
293             }
294
295             if (
296                 agreement_licenses.filter(al => al.status == "controlling")
297                     .length > 1
298             ) {
299                 errors.push(this.$__("Only one controlling license is allowed"))
300             }
301
302             let documents_with_uploaded_files = agreement.documents.filter(
303                 doc => typeof doc.file_content !== "undefined"
304             )
305             if (
306                 documents_with_uploaded_files.filter(
307                     doc => atob(doc.file_content).length >= max_allowed_packet
308                 ).length >= 1
309             ) {
310                 errors.push(
311                     this.$__("File size exceeds maximum allowed: %s MB").format(
312                         (max_allowed_packet / (1024 * 1024)).toFixed(2)
313                     )
314                 )
315             }
316             errors.forEach(function (e) {
317                 setWarning(e)
318             })
319             return !errors.length
320         },
321         onSubmit(e) {
322             e.preventDefault()
323
324             //let agreement= Object.assign( {} ,this.agreement); // copy
325             let agreement = JSON.parse(JSON.stringify(this.agreement)) // copy
326
327             if (!this.checkForm(agreement)) {
328                 return false
329             }
330
331             let apiUrl = "/api/v1/erm/agreements"
332
333             let method = "POST"
334             if (agreement.agreement_id) {
335                 method = "PUT"
336                 apiUrl += "/" + agreement.agreement_id
337             }
338             delete agreement.agreement_id
339             delete agreement.vendor
340             agreement.is_perpetual = agreement.is_perpetual ? true : false
341
342             if (agreement.vendor_id == "") {
343                 agreement.vendor_id = null
344             }
345
346             agreement.periods = agreement.periods.map(
347                 ({ agreement_id, agreement_period_id, ...keepAttrs }) =>
348                     keepAttrs
349             )
350
351             agreement.user_roles = agreement.user_roles.map(
352                 ({ patron, patron_str, ...keepAttrs }) => keepAttrs
353             )
354
355             agreement.agreement_licenses = agreement.agreement_licenses.map(
356                 ({
357                     license,
358                     agreement_id,
359                     agreement_license_id,
360                     ...keepAttrs
361                 }) => keepAttrs
362             )
363
364             agreement.agreement_relationships =
365                 agreement.agreement_relationships.map(
366                     ({ related_agreement, ...keepAttrs }) => keepAttrs
367                 )
368
369             agreement.documents = agreement.documents.map(
370                 ({ file_type, uploaded_on, ...keepAttrs }) => keepAttrs
371             )
372
373             delete agreement.agreement_packages
374
375             const options = {
376                 method: method,
377                 body: JSON.stringify(agreement),
378                 headers: {
379                     "Content-Type": "application/json;charset=utf-8",
380                 },
381             }
382
383             fetch(apiUrl, options)
384                 .then(response => checkError(response, 1))
385                 .then(response => {
386                     if (response.status == 200) {
387                         this.$router.push("/cgi-bin/koha/erm/agreements")
388                         setMessage(this.$__("Agreement updated"))
389                     } else if (response.status == 201) {
390                         this.$router.push("/cgi-bin/koha/erm/agreements")
391                         setMessage(this.$__("Agreement created"))
392                     } else {
393                         setError(response.message || response.statusText)
394                     }
395                 })
396                 .catch(error => {
397                     setError(error)
398                 })
399         },
400         onStatusChanged(e) {
401             if (e.authorised_value != "closed") {
402                 this.agreement.closure_reason = ""
403             }
404         },
405     },
406     components: {
407         AgreementPeriods,
408         UserRoles,
409         AgreementLicenses,
410         AgreementRelationships,
411         Documents,
412     },
413     name: "AgreementsFormAdd",
414 }
415 </script>