Created
March 27, 2019 18:14
-
-
Save reppair/818c74b623eb1bd9246a74e31e7cf821 to your computer and use it in GitHub Desktop.
Vehile Intake Report
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// get a translation from the stack by it's group and key | |
function getTranslation(group_dot_key_string) { | |
let [group, key] = group_dot_key_string.split('.'); | |
if (Oxy.translations[group] === undefined || Oxy.translations[group][key] === undefined) { | |
return group_dot_key_string; | |
} | |
return Oxy.translations[group][key]; | |
} | |
export default { | |
methods: { | |
// log the user out | |
logout(event) { | |
axios.post(event.target.href) | |
.then(response => document.location = response.data.redirect_to) | |
.catch(errors => console.log(errors)); | |
}, | |
// Get a translation string | |
trans(group_dot_key_string) { | |
return getTranslation(group_dot_key_string); | |
}, | |
// scroll to the first error on the form | |
scrollToFirstError(selector = '.invalid-feedback') { | |
let elements = Array.from(document.querySelectorAll(selector)).filter(el => { | |
return el.style['display'] !== 'none'; | |
}); | |
if (elements.length) elements[0].parentElement.scrollIntoView(); | |
}, | |
// 🐿 ship it | |
async submitForm(method, url, data, headers = {}, thenCallback = false, catchCallback = false) { | |
const isValid = await this.$validator.validate(), | |
errorBag = this.$validator.errors; | |
// guard for validation failures | |
if (!isValid) { | |
this.scrollToFirstError(); | |
return; | |
} | |
let promise = axios({ | |
method: method, | |
url: url, | |
data: data, | |
headers: headers, | |
}); | |
if (thenCallback) { | |
promise.then(thenCallback); | |
} else { | |
promise.then(response => window.location = response.data.redirect_to); | |
} | |
if (catchCallback) { | |
promise.catch(catchCallback) | |
} else { | |
promise.catch(error => { | |
// handle server side validation errors | |
if (error.response.status === 422) { | |
let errors = error.response.data.errors; | |
Object.keys(errors).forEach(field => { | |
errorBag.add({ | |
field: field, | |
msg: errors[field][0], | |
}); | |
}); | |
this.scrollToFirstError(); | |
} | |
}); | |
} | |
}, | |
cl(loggable) { | |
console.log(loggable); | |
}, | |
}, | |
filters: { | |
toEuro: value => { | |
return '€ ' + parseFloat(Number(value) / 100); | |
}, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<form @submit.prevent="createReport()"> | |
<!-- client --> | |
<div class="relative"> | |
<v-select :options="clientOptions" | |
:placeholder="trans('labels.select-client')" | |
v-model="client" | |
class="mb-4 v-select-bb" | |
:disabled="disabled" | |
name="client" | |
v-validate="'required'" | |
></v-select> | |
<small class="invalid-feedback absolute pin-r pin-t pt-2 pr-6" | |
v-show="errors.has('client')" | |
v-text="errors.first('client')" | |
></small> | |
</div> | |
<alpr classes="input input-bb flex justify-between mb-4" | |
:placeholder="trans('labels.upload-plate-number-photo')" | |
v-on:plate-photo-uploaded="platePhotoUploaded($event)" | |
v-on:plate-recognised="plateRecognised($event)" | |
v-on:plate-not-recognised="clearAlprInputs()" | |
></alpr> | |
<!-- plate number --> | |
<text-input name="plate_number" | |
:placeholder="trans('labels.plate-number')" | |
v-model="plate_number" | |
v-validate="'required'" | |
classes="input input-bb mb-4" | |
:disabled="disabled" | |
></text-input> | |
<!-- brand --> | |
<text-input name="brand" | |
:placeholder="trans('labels.brand')" | |
v-model="brand" | |
v-validate="'required'" | |
classes="input input-bb mb-4" | |
:disabled="disabled" | |
></text-input> | |
<!-- model --> | |
<text-input name="model" | |
:placeholder="trans('labels.model')" | |
v-model="model" | |
v-validate="'required'" | |
classes="input input-bb mb-4" | |
:disabled="disabled" | |
></text-input> | |
<!-- color --> | |
<text-input name="color" | |
:placeholder="trans('labels.color')" | |
v-model="color" | |
v-validate="'required'" | |
classes="input input-bb mb-4" | |
:disabled="disabled" | |
></text-input> | |
<!-- mileage --> | |
<text-input name="mileage" | |
:placeholder="trans('labels.mileage')" | |
v-model="mileage" | |
v-validate="'required'" | |
classes="input input-bb mb-4" | |
:disabled="disabled" | |
></text-input> | |
<!-- chassis number --> | |
<text-input name="chassis_number" | |
:placeholder="trans('labels.chassis-number')" | |
v-model="chassis_number" | |
v-validate="'required'" | |
classes="input input-bb mb-4" | |
:disabled="disabled" | |
></text-input> | |
<!-- gate in --> | |
<div class="relative"> | |
<datepicker v-model="gate_in" | |
input-class="input input-bb mb-4" | |
:placeholder="trans('labels.gate-in')" | |
:disabled="disabled" | |
name="gate_in" | |
v-validate="'required'" | |
></datepicker> | |
<small class="invalid-feedback absolute pin-r pin-t pt-2" | |
v-show="errors.has('gate_in')" | |
v-text="errors.first('gate_in')" | |
></small> | |
</div> | |
<!-- newly added photos list --> | |
<div class="row photos-container"></div> | |
<!-- photos --> | |
<input type="file" multiple accept="image/*" class="hidden" id="photos" @change="photosAdded($event)"> | |
<label for="photos" class="input input-bb mb-4 flex justify-between"> | |
{{ trans('labels.upload-report-photos') }} | |
<i class="fas fa-camera ml-2"></i> | |
</label> | |
<div class="row"> | |
<!-- todo: loop original photos if used for edit --> | |
<!--<div class="col-12 md:col-4" v-for="image in media">--> | |
<!--<img src="" alt="">--> | |
<!--</div>--> | |
</div> | |
<div class="text-right pt-4"> | |
<button type="submit" class="btn btn-blue" :disabled="disabled">Create Report</button> | |
</div> | |
</form> | |
</template> | |
<script> | |
import CommonVueMethods from "../CommonVueMethods"; | |
import TextInput from "../components/TextInput"; | |
import vSelect from 'vue-select/src/components/Select'; | |
import Datepicker from 'vuejs-datepicker'; | |
import Alpr from './Alpr'; | |
export default { | |
name: 'vehicle-details-form', | |
extends: CommonVueMethods, | |
components: { | |
TextInput, | |
vSelect, | |
Datepicker, | |
Alpr, | |
}, | |
props: ['action', 'report', 'clients'], | |
data() { | |
return { | |
disabled: false, | |
client: this.getReportAttribute('client', null), | |
plate_number: this.getReportAttribute('plate_number'), | |
chassis_number: this.getReportAttribute('chassis_number'), | |
brand: this.getReportAttribute('brand'), | |
model: this.getReportAttribute('model'), | |
mileage: this.getReportAttribute('mileage'), | |
color: this.getReportAttribute('color'), | |
gate_in: this.getReportAttribute('gate_in', null), | |
photos: [], | |
plate_photo: null, // Alpr event | |
alpr_results: null, // Alpr event | |
}; | |
}, | |
methods: { | |
// get an attribute from the report or default value | |
getReportAttribute(attr, fallback = '') { | |
if (typeof this.$props.report === 'undefined') { | |
return fallback; | |
} | |
switch (attr) { | |
case 'client': | |
return this.getReportClientOption(); | |
case 'gate_in': | |
return this.getReportGateIn(); | |
default: | |
return this.$props.report[attr]; | |
} | |
}, | |
// get the client select option when loading existing report | |
getReportClientOption() { | |
return {label: this.$props.report.client.name, value: this.$props.report.client.id} | |
}, | |
// get the gate as date from the report attributes | |
getReportGateIn() { | |
let date = this.$props.report.gate_in_date.split('-'); | |
return new Date(date[0], date[1], date[2]); | |
}, | |
// add the photo/s to the photos prop & visualise them | |
photosAdded(event) { | |
let files = Array.from(event.target.files); | |
files.forEach(file => { | |
// append the file | |
this.photos.push(file); | |
// visualise it | |
let div = document.createElement('div'), | |
img = document.createElement('img'), | |
reader = new FileReader(); | |
div.classList.add('col-12', 'md:col-4', 'delete-button'); // todo: add delete btn as after | |
reader.onload = e => img.src = e.target.result; | |
reader.readAsDataURL(file); | |
div.appendChild(img); | |
document.querySelector('.photos-container').appendChild(div); | |
}); | |
}, | |
// When a plate photo is uploaded | |
platePhotoUploaded(event) { | |
this.plate_photo = event; | |
// this.clearAlprInputs(); | |
}, | |
// When the ALPR recognises a plate | |
plateRecognised(data) { | |
this.alpr_results = data; | |
this.plate_number = data.plate; | |
this.brand = data.brand[0].name; | |
this.model = data.model[0].name; | |
this.color = data.color[0].name; | |
}, | |
// Clear all ALPR related input fields | |
clearAlprInputs() { | |
this.plate_number = ''; | |
this.brand = ''; | |
this.model = ''; | |
this.color = ''; | |
}, | |
// send request to create a report | |
createReport() { | |
let details = new FormData(), | |
append = ['plate_number', 'chassis_number', 'brand', 'model', 'mileage', 'color']; | |
append.forEach(attr => details.append(attr, this[attr])); | |
if (this.client) details.append('client_id', this.client.value); | |
if (this.gate_in) details.append('gate_in', this.gate_in.toISOString().split('T')[0]); | |
if (this.plate_photo) details.append('plate_photo', this.plate_photo); | |
if (this.photos.length) { | |
this.photos.forEach(file => { | |
details.append('photos[]', file); | |
}); | |
} | |
this.submitForm('post', this.action, details); | |
}, | |
}, | |
computed: { | |
// options for the client select | |
clientOptions() { | |
return _.map(this.$props.clients, c => { | |
return {value: c.id, label: c.name}; | |
}); | |
} | |
} | |
} | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<form @submit.prevent="updateReport()" class="pb-8"> | |
<h2 class="mb-4">Primary check</h2> | |
<!-- part --> | |
<v-select :options="partOptions" | |
:placeholder="trans('labels.select-part')" | |
v-model="part" | |
class="mb-4 v-select-bb" | |
@change="activity = null" | |
></v-select> | |
<!-- activity --> | |
<v-select :options="activityOptions" | |
:placeholder="trans('labels.select-activity')" | |
v-model="activity" | |
class="mb-4 v-select-bb" | |
> | |
<span slot="no-options">Please, select a part first.</span> | |
</v-select> | |
<!-- ACP price checkboxes --> | |
<div class="row" v-if="activity_client_part"> | |
<label class="col-12 md:col-6 flex" v-if="activity_client_part.labor_price"> | |
<input class="mr-2" | |
type="checkbox" | |
:value="activity_client_part.labor_price" | |
v-model="include_labor_price" | |
> | |
<span :class="{'line-through': !include_labor_price}"> | |
Include labor price: {{ activity_client_part.labor_price | toEuro }} | |
</span> | |
</label> | |
<label class="col-12 md:col-6 flex" v-if="activity_client_part.preparation_price"> | |
<input class="mr-2" | |
type="checkbox" | |
:value="activity_client_part.preparation_price" | |
v-model="include_preparation_price" | |
> | |
<span :class="{'line-through': !include_preparation_price}"> | |
Include preparation price: {{ activity_client_part.preparation_price | toEuro }} | |
</span> | |
</label> | |
</div> | |
<!-- validation error if no primary check is selected --> | |
<small class="invalid-feedback py-2" | |
v-show="errors.has('primary_check')" | |
v-text="errors.first('primary_check')" | |
></small> | |
<!-- add ACP --> | |
<div class="text-right pt-4"> | |
<button class="btn btn-blue" | |
:disabled="!canAddPrimaryCheck" | |
@click="addPrimaryCheck()" | |
type="button" | |
> | |
Add | |
</button> | |
</div> | |
<!-- primary checks list --> | |
<table class="w-full my-4"> | |
<tr class="text-left border-b border-blue"> | |
<th class="py-2">Part</th> | |
<th class="py-2">Activity</th> | |
<th class="py-2">Labor</th> | |
<th class="py-2">Preparation</th> | |
</tr> | |
<tr v-for="check in primary_checks" class="py-2"> | |
<td class="py-2">{{ check.acp.part.name }}</td> | |
<td class="py-2">{{ check.acp.activity.name }}</td> | |
<td class="py-2">{{ check.labor_price | toEuro }}</td> | |
<td class="py-2 relative"> | |
{{ check.preparation_price | toEuro }} | |
<!-- remove row button --> | |
<button class="absolute pin-r text-red" @click="removePrimaryCheck(check)">x</button> | |
</td> | |
</tr> | |
<!-- total --> | |
<tr> | |
<td class="pt-4 pb-2"></td> | |
<td class="pt-4 pb-2"></td> | |
<td class="pt-4 pb-2"></td> | |
<td class="pt-4 pb-2 font-bold border-b border-blue flex justify-between"> | |
<span>Total:</span> | |
<span>{{ primaryChecksTotal | toEuro }}</span> | |
</td> | |
</tr> | |
</table> | |
<h2 class="mb-4">Secondary check</h2> | |
<!-- secondary check --> | |
<v-select :options="secondaryCheckOptions" | |
:placeholder="trans('labels.select-secondary-check')" | |
v-model="secondary_check" | |
class="mb-4 v-select-bb" | |
></v-select> | |
<!-- secondary check value --> | |
<div v-if="secondary_check" class="row"> | |
<label class="col-6 text-right"> | |
<input type="radio" | |
class="mr-2" | |
v-model="secondary_check.value" | |
value="value_1" | |
> {{ secondary_check.model.value_1 }} | |
</label> | |
<label class="col-6"> | |
<input type="radio" | |
class="mr-2" | |
v-model="secondary_check.value" | |
value="value_2" | |
> {{ secondary_check.model.value_2 }} | |
</label> | |
</div> | |
<!-- add secondary check button --> | |
<div class="text-right pt-4"> | |
<button class="btn btn-blue" | |
:disabled="!secondary_check || !secondary_check.value" | |
@click="addSecondaryCheck()" | |
type="button" | |
> | |
Add | |
</button> | |
</div> | |
<!-- secondary checks list --> | |
<ul class="pl-4 my-4"> | |
<li v-for="sc in secondary_checks" class="py-2 relative"> | |
{{ sc.model.name }}: | |
<strong v-text="sc.model[sc.value]"></strong> | |
<button class="absolute pin-r text-red pl-2 -mt-1" @click="removeSecondaryCheck(sc)">x</button> | |
</li> | |
</ul> | |
<h2 class="mb-4">Tire profile</h2> | |
<!-- tire profile inputs --> | |
<div class="row"> | |
<!-- front left tire --> | |
<div class="col-12 md:col-6"> | |
<text-input name="tire_profile_fl" | |
:placeholder="trans('labels.front-left-tire')" | |
v-model="tire_profile['fl']" | |
classes="input input-bb mb-4" | |
v-validate="'required|numeric|min_value:0|max_value:30'" | |
></text-input> | |
</div> | |
<!-- front right tire --> | |
<div class="col-12 md:col-6"> | |
<text-input name="tire_profile_fr" | |
:placeholder="trans('labels.front-right-tire')" | |
v-model="tire_profile['fr']" | |
classes="input input-bb mb-4" | |
v-validate="'required|numeric|min_value:0|max_value:30'" | |
></text-input> | |
</div> | |
<!-- rear left tire --> | |
<div class="col-12 md:col-6"> | |
<text-input name="tire_profile_rl" | |
:placeholder="trans('labels.rear-left-tire')" | |
v-model="tire_profile['rl']" | |
classes="input input-bb mb-4" | |
v-validate="'required|numeric|min_value:0|max_value:30'" | |
></text-input> | |
</div> | |
<!-- rear right tire --> | |
<div class="col-12 md:col-6"> | |
<text-input name="tire_profile_rr" | |
:placeholder="trans('labels.rear-right-tire')" | |
v-model="tire_profile['rr']" | |
classes="input input-bb mb-4" | |
v-validate="'required|numeric|min_value:0|max_value:30'" | |
></text-input> | |
</div> | |
</div> | |
<h2 class="my-4">Other activities</h2> | |
<!-- select other activity --> | |
<v-select :options="otherActivityOptions" | |
:placeholder="trans('labels.select-other-activity')" | |
v-model="other_activity" | |
class="mb-4 v-select-bb" | |
></v-select> | |
<!-- other activity value (optional if the activity has both repair and replacement prices) --> | |
<div v-if="other_activity && other_activity.multiple_values" class="row"> | |
<label class="col-6 text-right"> | |
<input type="radio" | |
class="mr-2" | |
v-model="other_activity.value" | |
value="replacement_price" | |
> Replacement: {{ other_activity.model.replacement_price | toEuro }} | |
</label> | |
<label class="col-6"> | |
<input type="radio" | |
class="mr-2" | |
v-model="other_activity.value" | |
value="repair_price" | |
> Repair: {{ other_activity.model.repair_price | toEuro }} | |
</label> | |
</div> | |
<!-- add other activity button --> | |
<div class="text-right pt-4"> | |
<button class="btn btn-blue" | |
:disabled="!canAddOtherActivity" | |
@click="addOtherActivity()" | |
type="button" | |
> | |
Add | |
</button> | |
</div> | |
<!-- primary checks list --> | |
<table class="w-full my-4"> | |
<tr class="text-left border-b border-blue"> | |
<th class="py-2">Activity</th> | |
<th class="py-2">Type</th> | |
<th class="py-2">Price</th> | |
</tr> | |
<tr v-for="activity in other_activities" class="py-2"> | |
<td class="py-2">{{ activity.model.name }}</td> | |
<td class="py-2 capitalize">{{ activity.value.replace('_price', '') }}</td> | |
<td class="py-2 relative"> | |
{{ activity.model[activity.value] | toEuro }} | |
<!-- remove row button --> | |
<button class="absolute pin-r text-red" @click="removeOtherActivity(activity)">x</button> | |
</td> | |
</tr> | |
<!-- total --> | |
<tr> | |
<td class="pt-4 pb-2"></td> | |
<td class="pt-4 pb-2"></td> | |
<td class="pt-4 pb-2 font-bold border-b border-blue flex justify-between"> | |
<span>Total:</span> | |
<span>{{ otherActivitiesTotal | toEuro }}</span> | |
</td> | |
</tr> | |
</table> | |
<!-- the total coast for the whole report --> | |
<div class="flex py-4 mt-4 text-white font-bold"> | |
<div class="w-1/3 ml-auto p-4 bg-blue"> | |
<div class="flex justify-between border-b border-white pb-1"> | |
<span>Total coast:</span> | |
<span>{{ totalCoast | toEuro }}</span> | |
</div> | |
</div> | |
</div> | |
<!-- submit form button --> | |
<div class="text-right pt-4"> | |
<button class="btn btn-blue" type="submit"> | |
Save Report | |
</button> | |
</div> | |
</form> | |
</template> | |
<script> | |
import CommonVueMethods from "../CommonVueMethods"; | |
import vSelect from 'vue-select/src/components/Select'; | |
import TextInput from "../components/TextInput"; | |
export default { | |
name: 'vehicle-inspection-form', | |
extends: CommonVueMethods, | |
components: { | |
vSelect, | |
TextInput, | |
}, | |
props: [ | |
'action', | |
'report', | |
'activity_client_parts', | |
'parts', | |
'activities', | |
'secondary_checks_list', | |
'client_other_activities', | |
], | |
data() { | |
return { | |
part: null, | |
activity: null, | |
activity_client_part: null, | |
include_labor_price: false, | |
include_preparation_price: false, | |
primary_checks: [], | |
secondary_check: null, | |
secondary_checks: [], // todo: should all of those be marked? | |
secondaryCheckOptions: [], | |
tire_profile: { | |
fl: null, | |
fr: null, | |
rl: null, | |
rr: null, | |
}, | |
other_activity: null, | |
otherActivityOptions: [], | |
other_activities: [], | |
}; | |
}, | |
mounted() { | |
// set the secondary check select options | |
this.secondaryCheckOptions = _.map(this.$props.secondary_checks_list, (sc, id) => { | |
return { | |
id: id, | |
label: sc.name, | |
model: sc, | |
value: null, | |
}; | |
}); | |
// set the other activity select options | |
this.otherActivityOptions = _.map(this.$props.client_other_activities, (oa, id) => { | |
let activity = { | |
id: id, | |
label: oa.name, | |
model: oa, | |
value: null, | |
multiple_values: oa.replacement_price !== null && oa.repair_price !== null, | |
}; | |
// if the activity has one value set it here (if not set it with a radio button) | |
if (!activity.multiple_values) { | |
activity.value = oa.repair_price !== null ? 'repair_price' : 'replacement_price' | |
} | |
return activity; | |
}); | |
// todo: repeat with the primary check options | |
}, | |
watch: { | |
// on activity change | |
activity(activity) { | |
// clear input without the selected part & activity | |
this.resetPrimaryCheckInput(null, this.part, activity); | |
if (!activity) return; | |
// find the ACP and set it so it's prices can be shown for selection | |
this.activity_client_part = this.$props.activity_client_parts.find(acp => { | |
return acp.activity_id == activity.id && acp.part_id == this.part.id; | |
}); | |
// pre-check the include labor||preparation price | |
this.activity_client_part.labor_price | |
? this.include_labor_price = true | |
: this.include_preparation_price = true; | |
}, | |
}, | |
methods: { | |
// add ACP to the primary checks list | |
addPrimaryCheck() { | |
// todo: guard here for the same ACP being added | |
this.primary_checks.push({ | |
acp: this.activity_client_part, | |
include_labor_price: this.include_labor_price, | |
labor_price: this.include_labor_price ? this.activity_client_part.labor_price : 0, | |
include_preparation_price: this.include_preparation_price, | |
preparation_price: this.include_preparation_price ? this.activity_client_part.preparation_price : 0, | |
}); | |
// and reset the inputs | |
this.resetPrimaryCheckInput(); | |
}, | |
// remove a primary check from the list | |
removePrimaryCheck(check) { | |
this.primary_checks = this.primary_checks.filter(c => c.acp.id != check.acp.id); | |
}, | |
// reset all inputs and the selected ACP | |
resetPrimaryCheckInput(acp = null, part = null, activity = null, labor = false, preparation = false) { | |
this.activity_client_part = acp; | |
this.part = part; | |
this.activity = activity; | |
this.include_labor_price = labor; | |
this.include_preparation_price = preparation; | |
}, | |
// add secondary check to the list | |
addSecondaryCheck() { | |
this.secondary_checks.push(this.secondary_check); | |
// find and remove this check from the select options | |
this.secondaryCheckOptions = this.secondaryCheckOptions.filter(option => { | |
return option.id != this.secondary_check.id; | |
}); | |
this.secondary_check = null; | |
}, | |
// remove a secondary check from the list | |
removeSecondaryCheck(check) { | |
// first push it back to the secondaryCheckOptions array for selection | |
check.value = null; | |
this.secondaryCheckOptions.push(check); | |
this.secondaryCheckOptions = _.sortBy(this.secondaryCheckOptions, 'id'); | |
// remove | |
this.secondary_checks = this.secondary_checks.filter(sc => sc.model.id != check.model.id); | |
}, | |
// add other activity to the list | |
addOtherActivity() { | |
this.other_activities.push(this.other_activity); | |
// remove it from the selectable options | |
this.otherActivityOptions = this.otherActivityOptions.filter(option => { | |
return option.model.id !== this.other_activity.model.id; | |
}); | |
// and clear it | |
this.other_activity = null; | |
}, | |
// remove an other activity from the list | |
removeOtherActivity(activity) { | |
// push it back to the otherActivityOptions list first so it can be re-selected | |
if (activity.multiple_values) activity.value = null; | |
this.otherActivityOptions.push(activity); | |
this.otherActivityOptions = _.sortBy(this.otherActivityOptions, 'id'); | |
// remove | |
this.other_activities = this.other_activities.filter(a => a.model.id !== activity.model.id); | |
}, | |
// send request to create a report | |
updateReport() { | |
this.$validator.errors.clear(); | |
// validate min:1 primary check is required | |
if (!this.primary_checks.length) { | |
this.$validator.errors.add({ | |
field: 'primary_check', | |
msg: this.trans('db.at-least-one-primary-check-is-required'), | |
}); | |
} | |
let data = { | |
primary_checks: [], | |
secondary_checks: [], | |
tire_profile: this.tire_profile, | |
other_activities: [], | |
}; | |
this.primary_checks.forEach(check => data.primary_checks.push({ | |
id: check.acp.id, | |
include_labor: check.include_labor_price, | |
include_preparation: check.include_preparation_price, | |
})); | |
['secondary_checks', 'other_activities'].forEach(key => { | |
this[key].forEach(item => data[key].push({ | |
id: item.model.id, | |
value: item.value, | |
})); | |
}); | |
this.submitForm('patch', this.$props.action, data); | |
}, | |
}, | |
computed: { | |
// part select options | |
partOptions() { | |
return _.map(this.$props.parts, (value, id) => { | |
return {id: id, label: value}; | |
}); | |
}, | |
// activity select options | |
activityOptions() { | |
if (!this.part) return []; | |
return _.map(this.$props.activities[this.part.id], (value, id) => { | |
return {id: id, label: value}; | |
}); | |
}, | |
// determine if a primary check can be added to the list | |
canAddPrimaryCheck() { | |
return (this.include_labor_price || this.include_preparation_price) | |
&& this.part != null | |
&& this.activity != null; | |
}, | |
// get the total price of the primary checks | |
primaryChecksTotal() { | |
if (!this.primary_checks.length) return 0; | |
let total = 0; | |
this.primary_checks.forEach(check => { | |
total = check.include_labor_price ? total + check.acp.labor_price : total; | |
total = check.include_preparation_price ? total + check.acp.preparation_price : total; | |
}); | |
return total; | |
}, | |
// determine if an other activity can be added to the list | |
canAddOtherActivity() { | |
if (this.other_activity === null) return false; | |
return this.other_activity.multiple_values | |
? this.other_activity.value !== null | |
: this.other_activity !== null; | |
}, | |
// the total price for other activities | |
otherActivitiesTotal() { | |
let total = 0; | |
this.other_activities.forEach(a => total = total + Number(a.model[a.value])); | |
return total; | |
}, | |
// the total coast of the whole report | |
totalCoast() { | |
return this.primaryChecksTotal + this.otherActivitiesTotal; | |
} | |
}, | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment