add fuel subtype to fillup
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
</a> -->
|
</a> -->
|
||||||
|
|
||||||
<h1 align="center" style="margin-bottom:0">Hammond</h1>
|
<h1 align="center" style="margin-bottom:0">Hammond</h1>
|
||||||
<p align="center">Current Version - 2021.07.14</p>
|
<p align="center">Current Version - 2021.07.23</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
A self-hosted vehicle expense tracking system with support for multiple users.
|
A self-hosted vehicle expense tracking system with support for multiple users.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func RegisterVehicleController(router *gin.RouterGroup) {
|
|||||||
router.GET("/me/stats", getMystats)
|
router.GET("/me/stats", getMystats)
|
||||||
|
|
||||||
router.GET("/vehicles/:id/fillups", getFillupsByVehicleId)
|
router.GET("/vehicles/:id/fillups", getFillupsByVehicleId)
|
||||||
|
router.GET("/vehicles/:id/fuelSubTypes", getFuelSubTypesByVehicleId)
|
||||||
router.POST("/vehicles/:id/fillups", createFillup)
|
router.POST("/vehicles/:id/fillups", createFillup)
|
||||||
router.GET("/vehicles/:id/fillups/:subId", getFillupById)
|
router.GET("/vehicles/:id/fillups/:subId", getFillupById)
|
||||||
router.PUT("/vehicles/:id/fillups/:subId", updateFillup)
|
router.PUT("/vehicles/:id/fillups/:subId", updateFillup)
|
||||||
@@ -121,6 +122,22 @@ func getFillupsByVehicleId(c *gin.Context) {
|
|||||||
c.JSON(http.StatusUnprocessableEntity, common.NewValidatorError(err))
|
c.JSON(http.StatusUnprocessableEntity, common.NewValidatorError(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func getFuelSubTypesByVehicleId(c *gin.Context) {
|
||||||
|
|
||||||
|
var searchByIdQuery models.SearchByIdQuery
|
||||||
|
|
||||||
|
if err := c.ShouldBindUri(&searchByIdQuery); err == nil {
|
||||||
|
|
||||||
|
fuelSubtypes, err := service.GetDistinctFuelSubtypesForVehicle(searchByIdQuery.Id)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, common.NewError("getFuelSubTypesByVehicleId", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, fuelSubtypes)
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, common.NewValidatorError(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getExpensesByVehicleId(c *gin.Context) {
|
func getExpensesByVehicleId(c *gin.Context) {
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ type Fillup struct {
|
|||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
DistanceUnit DistanceUnit `json:"distanceUnit"`
|
DistanceUnit DistanceUnit `json:"distanceUnit"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
FuelSubType string `json:"fuelSubType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Fillup) FuelUnitDetail() EnumDetail {
|
func (v *Fillup) FuelUnitDetail() EnumDetail {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ type CreateFillupRequest struct {
|
|||||||
FillingStation string `form:"fillingStation" json:"fillingStation"`
|
FillingStation string `form:"fillingStation" json:"fillingStation"`
|
||||||
UserID string `form:"userId" json:"userId" binding:"required"`
|
UserID string `form:"userId" json:"userId" binding:"required"`
|
||||||
Date time.Time `form:"date" json:"date" binding:"required" time_format:"2006-01-02"`
|
Date time.Time `form:"date" json:"date" binding:"required" time_format:"2006-01-02"`
|
||||||
|
FuelSubType string `form:"fuelSubType" json:"fuelSubType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateFillupRequest struct {
|
type UpdateFillupRequest struct {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ func CreateFillup(model models.CreateFillupRequest) (*db.Fillup, error) {
|
|||||||
Date: model.Date,
|
Date: model.Date,
|
||||||
Currency: user.Currency,
|
Currency: user.Currency,
|
||||||
DistanceUnit: user.DistanceUnit,
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
FuelSubType: model.FuelSubType,
|
||||||
Source: "API",
|
Source: "API",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +197,7 @@ func UpdateFillup(fillupId string, model models.UpdateFillupRequest) error {
|
|||||||
Comments: model.Comments,
|
Comments: model.Comments,
|
||||||
FillingStation: model.FillingStation,
|
FillingStation: model.FillingStation,
|
||||||
UserID: model.UserID,
|
UserID: model.UserID,
|
||||||
|
FuelSubType: model.FuelSubType,
|
||||||
Date: model.Date,
|
Date: model.Date,
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
@@ -235,6 +237,11 @@ func GetVehicleAttachments(vehicleId string) (*[]db.Attachment, error) {
|
|||||||
|
|
||||||
return db.GetVehicleAttachments(vehicleId)
|
return db.GetVehicleAttachments(vehicleId)
|
||||||
}
|
}
|
||||||
|
func GetDistinctFuelSubtypesForVehicle(vehicleId string) ([]string, error) {
|
||||||
|
var names []string
|
||||||
|
tx := db.DB.Model(&db.Fillup{}).Where("vehicle_id=? and fuel_sub_type is not null", vehicleId).Distinct().Pluck("fuel_sub_type", &names)
|
||||||
|
return names, tx.Error
|
||||||
|
}
|
||||||
|
|
||||||
func GetUserStats(userId string, model models.UserStatsQueryModel) ([]models.VehicleStatsModel, error) {
|
func GetUserStats(userId string, model models.UserStatsQueryModel) ([]models.VehicleStatsModel, error) {
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
faTrash,
|
faTrash,
|
||||||
faShare,
|
faShare,
|
||||||
faUserFriends,
|
faUserFriends,
|
||||||
|
faTimesCircle,
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ library.add(
|
|||||||
faTrash,
|
faTrash,
|
||||||
faShare,
|
faShare,
|
||||||
faUserFriends,
|
faUserFriends,
|
||||||
|
faTimesCircle
|
||||||
)
|
)
|
||||||
Vue.use(Buefy, {
|
Vue.use(Buefy, {
|
||||||
defaultIconComponent: 'vue-fontawesome',
|
defaultIconComponent: 'vue-fontawesome',
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default {
|
|||||||
quickEntry: null,
|
quickEntry: null,
|
||||||
myVehicles: [],
|
myVehicles: [],
|
||||||
users: [],
|
users: [],
|
||||||
|
fuelSubTypes: [],
|
||||||
selectedVehicle: this.vehicle,
|
selectedVehicle: this.vehicle,
|
||||||
fillupModel: this.fillup,
|
fillupModel: this.fillup,
|
||||||
processQuickEntry: false,
|
processQuickEntry: false,
|
||||||
@@ -46,6 +47,14 @@ export default {
|
|||||||
},
|
},
|
||||||
...mapState('users', ['me']),
|
...mapState('users', ['me']),
|
||||||
...mapState('vehicles', ['fuelUnitMasters', 'fuelTypeMasters', 'vehicles']),
|
...mapState('vehicles', ['fuelUnitMasters', 'fuelTypeMasters', 'vehicles']),
|
||||||
|
filteredFuelSubtypes() {
|
||||||
|
if (!this.fillupModel.fuelSubType) {
|
||||||
|
return this.fuelSubTypes
|
||||||
|
}
|
||||||
|
return this.fuelSubTypes.filter((option) => {
|
||||||
|
return option.toLowerCase().indexOf(this.fillupModel.fuelSubType.toLowerCase()) >= 0
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'fillupModel.fuelQuantity': function(old, newOne) {
|
'fillupModel.fuelQuantity': function(old, newOne) {
|
||||||
@@ -64,6 +73,7 @@ export default {
|
|||||||
this.myVehicles = this.vehicles
|
this.myVehicles = this.vehicles
|
||||||
this.selectedVehicle = this.vehicle
|
this.selectedVehicle = this.vehicle
|
||||||
this.fetchVehicleUsers()
|
this.fetchVehicleUsers()
|
||||||
|
this.fetchVehicleFuelSubTypes()
|
||||||
if (!this.fillup.id) {
|
if (!this.fillup.id) {
|
||||||
this.fillupModel = this.getEmptyFillup()
|
this.fillupModel = this.getEmptyFillup()
|
||||||
this.fillupModel.userId = this.me.id
|
this.fillupModel.userId = this.me.id
|
||||||
@@ -82,6 +92,14 @@ export default {
|
|||||||
})
|
})
|
||||||
.catch((err) => console.log(err))
|
.catch((err) => console.log(err))
|
||||||
},
|
},
|
||||||
|
fetchVehicleFuelSubTypes() {
|
||||||
|
store
|
||||||
|
.dispatch('vehicles/fetchFuelSubtypesByVehicleId', { vehicleId: this.selectedVehicle.id })
|
||||||
|
.then((data) => {
|
||||||
|
this.fuelSubTypes = data
|
||||||
|
})
|
||||||
|
.catch((err) => console.log(err))
|
||||||
|
},
|
||||||
getEmptyFillup() {
|
getEmptyFillup() {
|
||||||
return {
|
return {
|
||||||
vehicleId: this.selectedVehicle.id,
|
vehicleId: this.selectedVehicle.id,
|
||||||
@@ -96,6 +114,7 @@ export default {
|
|||||||
fillingStation: '',
|
fillingStation: '',
|
||||||
comments: '',
|
comments: '',
|
||||||
userId: '',
|
userId: '',
|
||||||
|
fuelSubType: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async createFillup() {
|
async createFillup() {
|
||||||
@@ -201,6 +220,17 @@ export default {
|
|||||||
>
|
>
|
||||||
</b-datepicker>
|
</b-datepicker>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
<b-field label="Fuel Subtype">
|
||||||
|
<b-autocomplete
|
||||||
|
v-model="fillupModel.fuelSubType"
|
||||||
|
:data="filteredFuelSubtypes"
|
||||||
|
placeholder="Octane etc."
|
||||||
|
clearable
|
||||||
|
autofocus
|
||||||
|
@select="(option) => (fillupModel.fuelSubType = option)"
|
||||||
|
>
|
||||||
|
</b-autocomplete>
|
||||||
|
</b-field>
|
||||||
<b-field label="Quantity*" addons>
|
<b-field label="Quantity*" addons>
|
||||||
<b-input v-model.number="fillupModel.fuelQuantity" type="number" step=".001" min="0" expanded required></b-input>
|
<b-input v-model.number="fillupModel.fuelQuantity" type="number" step=".001" min="0" expanded required></b-input>
|
||||||
<b-select v-model="fillupModel.fuelUnit" placeholder="Fuel Unit" required>
|
<b-select v-model="fillupModel.fuelUnit" placeholder="Fuel Unit" required>
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export default {
|
|||||||
<table class="table is-hoverable">
|
<table class="table is-hoverable">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Current Version</td>
|
<td>Current Version</td>
|
||||||
<td>2021.07.14</td>
|
<td>2021.07.23</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Website</td>
|
<td>Website</td>
|
||||||
|
|||||||
@@ -311,6 +311,9 @@ export default {
|
|||||||
<b-table-column v-slot="props" field="date" label="Date" :td-attrs="columnTdAttrs" sortable date>
|
<b-table-column v-slot="props" field="date" label="Date" :td-attrs="columnTdAttrs" sortable date>
|
||||||
{{ formatDate(props.row.date) }}
|
{{ formatDate(props.row.date) }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
|
<b-table-column v-slot="props" field="fuelSubType" label="Fuel Sub Type" :td-attrs="columnTdAttrs">
|
||||||
|
{{ props.row.fuelSubType }}
|
||||||
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="fuelQuantity" label="Qty." :td-attrs="hiddenMobile" numeric>
|
<b-table-column v-slot="props" field="fuelQuantity" label="Qty." :td-attrs="hiddenMobile" numeric>
|
||||||
{{ `${props.row.fuelQuantity} ${props.row.fuelUnitDetail.short}` }}
|
{{ `${props.row.fuelQuantity} ${props.row.fuelUnitDetail.short}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
|
|||||||
@@ -140,6 +140,12 @@ export const actions = {
|
|||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
fetchFuelSubtypesByVehicleId({ commit, state, rootState }, { vehicleId, force }) {
|
||||||
|
return axios.get(`/api/vehicles/${vehicleId}/fuelSubTypes`).then((response) => {
|
||||||
|
const data = response.data
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
},
|
||||||
fetchStatsByVehicleId({ commit, state, rootState }, { vehicleId, force }) {
|
fetchStatsByVehicleId({ commit, state, rootState }, { vehicleId, force }) {
|
||||||
if (state.vehicleStats.has(vehicleId) && !force) {
|
if (state.vehicleStats.has(vehicleId) && !force) {
|
||||||
return Promise.resolve(state.vehicleStats.get(vehicleId))
|
return Promise.resolve(state.vehicleStats.get(vehicleId))
|
||||||
|
|||||||
Reference in New Issue
Block a user