feat: Add excludeFromBilling option for diary dates and update related functionality
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
This commit is contained in:
@@ -18,14 +18,14 @@ const createDateForClub = async (req, res) => {
|
||||
try {
|
||||
const { clubId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { date, trainingStart, trainingEnd } = req.body;
|
||||
const { date, trainingStart, trainingEnd, excludeFromBilling } = req.body;
|
||||
if (!date) {
|
||||
throw new HttpError('The date field is required', 400);
|
||||
}
|
||||
if (isNaN(new Date(date).getTime())) {
|
||||
throw new HttpError('Invalid date format', 400);
|
||||
}
|
||||
const newDate = await diaryService.createDateForClub(userToken, clubId, date, trainingStart, trainingEnd);
|
||||
const newDate = await diaryService.createDateForClub(userToken, clubId, date, trainingStart, trainingEnd, excludeFromBilling);
|
||||
res.status(201).json(newDate);
|
||||
} catch (error) {
|
||||
console.error('[createDateForClub] - Error:', error);
|
||||
@@ -37,15 +37,22 @@ const updateTrainingTimes = async (req, res) => {
|
||||
try {
|
||||
const { clubId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { dateId, trainingStart, trainingEnd } = req.body;
|
||||
if (!dateId || !trainingStart) {
|
||||
devLog(dateId, trainingStart, trainingEnd);
|
||||
const { dateId, trainingStart, trainingEnd, excludeFromBilling } = req.body;
|
||||
if (!dateId) {
|
||||
devLog(dateId, trainingStart, trainingEnd, excludeFromBilling);
|
||||
throw new HttpError('notallfieldsfilled', 400);
|
||||
}
|
||||
const updatedDate = await diaryService.updateTrainingTimes(userToken, clubId, dateId, trainingStart, trainingEnd);
|
||||
const updatedDate = await diaryService.updateTrainingTimes(
|
||||
userToken,
|
||||
clubId,
|
||||
dateId,
|
||||
trainingStart,
|
||||
trainingEnd,
|
||||
excludeFromBilling,
|
||||
);
|
||||
|
||||
// Emit Socket-Event
|
||||
emitDiaryDateUpdated(clubId, dateId, { trainingStart, trainingEnd });
|
||||
emitDiaryDateUpdated(clubId, dateId, { trainingStart, trainingEnd, excludeFromBilling });
|
||||
|
||||
res.status(200).json(updatedDate);
|
||||
} catch (error) {
|
||||
|
||||
@@ -23,6 +23,12 @@ const DiaryDate = sequelize.define('DiaryDate', {
|
||||
trainingEnd: {
|
||||
type: DataTypes.TIME,
|
||||
allowNull: true,
|
||||
},
|
||||
excludeFromBilling: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
field: 'exclude_from_billing',
|
||||
}
|
||||
}, {
|
||||
tableName: 'diary_dates',
|
||||
|
||||
@@ -321,7 +321,8 @@ class BillingService {
|
||||
clubId,
|
||||
date: { [Op.between]: [periodStart, periodEnd] },
|
||||
trainingStart: { [Op.ne]: null },
|
||||
trainingEnd: { [Op.ne]: null }
|
||||
trainingEnd: { [Op.ne]: null },
|
||||
excludeFromBilling: false,
|
||||
},
|
||||
attributes: ['date', 'trainingStart', 'trainingEnd'],
|
||||
order: [['date', 'ASC'], ['trainingStart', 'ASC']]
|
||||
|
||||
@@ -26,7 +26,7 @@ class DiaryService {
|
||||
return dates;
|
||||
}
|
||||
|
||||
async createDateForClub(userToken, clubId, date, trainingStart, trainingEnd) {
|
||||
async createDateForClub(userToken, clubId, date, trainingStart, trainingEnd, excludeFromBilling = false) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const club = await Club.findByPk(clubId);
|
||||
if (!club) {
|
||||
@@ -44,12 +44,13 @@ class DiaryService {
|
||||
clubId,
|
||||
trainingStart: trainingStart || null,
|
||||
trainingEnd: trainingEnd || null,
|
||||
excludeFromBilling: Boolean(excludeFromBilling),
|
||||
});
|
||||
|
||||
return newDate;
|
||||
}
|
||||
|
||||
async updateTrainingTimes(userToken, clubId, dateId, trainingStart, trainingEnd) {
|
||||
async updateTrainingTimes(userToken, clubId, dateId, trainingStart, trainingEnd, excludeFromBilling) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const diaryDate = await DiaryDate.findOne({ where: { clubId, id: dateId } });
|
||||
if (!diaryDate) {
|
||||
@@ -60,6 +61,9 @@ class DiaryService {
|
||||
}
|
||||
diaryDate.trainingStart = trainingStart || null;
|
||||
diaryDate.trainingEnd = trainingEnd || null;
|
||||
if (excludeFromBilling !== undefined) {
|
||||
diaryDate.excludeFromBilling = Boolean(excludeFromBilling);
|
||||
}
|
||||
await diaryDate.save();
|
||||
return diaryDate;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,17 @@
|
||||
<label for="editTrainingEnd">{{ $t('diary.trainingEnd') }}:</label>
|
||||
<input id="editTrainingEnd" :value="trainingEnd" type="time" step="300" @input="$emit('update:training-end', $event.target.value)" />
|
||||
</div>
|
||||
<div class="diary-billing-toggle-wrap">
|
||||
<label class="diary-billing-toggle" for="editExcludeFromBilling">
|
||||
<input
|
||||
id="editExcludeFromBilling"
|
||||
type="checkbox"
|
||||
:checked="excludeFromBilling"
|
||||
@change="$emit('update:exclude-from-billing', $event.target.checked)"
|
||||
/>
|
||||
<span>Nicht abrechnen</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit">{{ $t('diary.updateTimes') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -120,11 +131,12 @@ export default {
|
||||
activitiesCount: { type: Number, default: 0 },
|
||||
trainingStart: { type: String, default: '' },
|
||||
trainingEnd: { type: String, default: '' },
|
||||
excludeFromBilling: { type: Boolean, default: false },
|
||||
groups: { type: Array, required: true },
|
||||
editingGroupId: { type: [Number, String, null], default: null },
|
||||
newGroupCount: { type: Number, default: 2 }
|
||||
},
|
||||
emits: ['toggle-panel', 'update-training-times', 'update:training-start', 'update:training-end', 'edit-group', 'update-group-field', 'save-group', 'cancel-edit-group', 'delete-group', 'update:new-group-count', 'create-groups']
|
||||
emits: ['toggle-panel', 'update-training-times', 'update:training-start', 'update:training-end', 'update:exclude-from-billing', 'edit-group', 'update-group-field', 'save-group', 'cancel-edit-group', 'delete-group', 'update:new-group-count', 'create-groups']
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -243,6 +255,20 @@ export default {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.diary-billing-toggle-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
|
||||
.diary-billing-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
font-weight: 600;
|
||||
color: #173042;
|
||||
}
|
||||
|
||||
.diary-groups-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
|
||||
@@ -68,6 +68,12 @@
|
||||
<label for="trainingEnd">{{ $t('diary.trainingEnd') }}:</label>
|
||||
<input type="time" step="300" id="trainingEnd" v-model="trainingEnd" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="excludeFromBilling" style="display: inline-flex; align-items: center; gap: 0.4rem;">
|
||||
<input type="checkbox" id="excludeFromBilling" v-model="excludeFromBilling" />
|
||||
<span>Nicht abrechnen</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit">{{ $t('diary.createDate') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -83,6 +89,7 @@
|
||||
:activities-count="activities.length"
|
||||
:training-start="trainingStart"
|
||||
:training-end="trainingEnd"
|
||||
:exclude-from-billing="excludeFromBilling"
|
||||
:groups="groups"
|
||||
:editing-group-id="editingGroupId"
|
||||
:new-group-count="newGroupCount"
|
||||
@@ -90,6 +97,7 @@
|
||||
@update-training-times="updateTrainingTimes"
|
||||
@update:training-start="trainingStart = $event"
|
||||
@update:training-end="trainingEnd = $event"
|
||||
@update:exclude-from-billing="excludeFromBilling = $event"
|
||||
@edit-group="editGroup"
|
||||
@update-group-field="updateGroupField"
|
||||
@save-group="saveGroup"
|
||||
@@ -977,6 +985,7 @@ export default {
|
||||
newDate: '',
|
||||
trainingStart: '',
|
||||
trainingEnd: '',
|
||||
excludeFromBilling: false,
|
||||
members: [],
|
||||
participants: [],
|
||||
showTrainingGroupDialog: false,
|
||||
@@ -1780,6 +1789,7 @@ export default {
|
||||
date: slot.date,
|
||||
trainingStart: slot.startTime || null,
|
||||
trainingEnd: slot.endTime || null,
|
||||
excludeFromBilling: false,
|
||||
});
|
||||
await this.refreshDates(post.data.id);
|
||||
await this.handleDateChange();
|
||||
@@ -1799,6 +1809,7 @@ export default {
|
||||
const dateData = response.data.find(entry => entry.id === dateId);
|
||||
this.trainingStart = dateData.trainingStart;
|
||||
this.trainingEnd = dateData.trainingEnd;
|
||||
this.excludeFromBilling = Boolean(dateData.excludeFromBilling);
|
||||
this.selectedActivityTags = dateData.diaryTags.map(tag => ({
|
||||
id: tag.id,
|
||||
name: tag.name
|
||||
@@ -1816,6 +1827,7 @@ export default {
|
||||
this.newDate = '';
|
||||
this.trainingStart = '';
|
||||
this.trainingEnd = '';
|
||||
this.excludeFromBilling = false;
|
||||
this.participants = [];
|
||||
}
|
||||
},
|
||||
@@ -1836,6 +1848,7 @@ export default {
|
||||
date: this.newDate,
|
||||
trainingStart: this.trainingStart || null,
|
||||
trainingEnd: this.trainingEnd || null,
|
||||
excludeFromBilling: this.excludeFromBilling,
|
||||
});
|
||||
this.dates.push({ id: response.data.id, date: response.data.date });
|
||||
// Liste nach Datum sortieren (neueste zuerst)
|
||||
@@ -1844,6 +1857,7 @@ export default {
|
||||
this.newDate = '';
|
||||
this.trainingStart = response.data.trainingStart;
|
||||
this.trainingEnd = response.data.trainingEnd;
|
||||
this.excludeFromBilling = Boolean(response.data.excludeFromBilling);
|
||||
// Direkt auf das leere Tagebuch des neuen Datums wechseln
|
||||
await this.handleDateChange();
|
||||
} catch (error) {
|
||||
@@ -1858,6 +1872,7 @@ export default {
|
||||
dateId,
|
||||
trainingStart: this.trainingStart || null,
|
||||
trainingEnd: this.trainingEnd || null,
|
||||
excludeFromBilling: this.excludeFromBilling,
|
||||
});
|
||||
this.showInfo(this.$t('messages.success'), this.$t('diary.trainingTimesUpdated'), '', 'success');
|
||||
} catch (error) {
|
||||
@@ -4332,6 +4347,9 @@ export default {
|
||||
if (data.updates.trainingEnd !== undefined) {
|
||||
this.trainingEnd = data.updates.trainingEnd;
|
||||
}
|
||||
if (data.updates.excludeFromBilling !== undefined) {
|
||||
this.excludeFromBilling = Boolean(data.updates.excludeFromBilling);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1588,6 +1588,7 @@ private fun DiaryListScreen(
|
||||
var newDiaryDateStr by rememberSaveable { mutableStateOf("") }
|
||||
var newDiaryStart by rememberSaveable { mutableStateOf("") }
|
||||
var newDiaryEnd by rememberSaveable { mutableStateOf("") }
|
||||
var newDiaryExcludeFromBilling by rememberSaveable { mutableStateOf(false) }
|
||||
val newDateScope = rememberCoroutineScope()
|
||||
var newDateScheduleGroups by remember { mutableStateOf<List<TrainingGroupDto>>(emptyList()) }
|
||||
var newDateScheduleLoading by remember { mutableStateOf(false) }
|
||||
@@ -1735,6 +1736,7 @@ private fun DiaryListScreen(
|
||||
newDiaryDateStr = kotlin.runCatching { java.time.LocalDate.now().toString() }.getOrElse { "" }
|
||||
newDiaryStart = diaryTimeForFormField(tmpl?.trainingStart).ifBlank { "17:30" }
|
||||
newDiaryEnd = diaryTimeForFormField(tmpl?.trainingEnd).ifBlank { "19:30" }
|
||||
newDiaryExcludeFromBilling = false
|
||||
showNewDateDialog = true
|
||||
},
|
||||
modifier = Modifier.heightIn(min = TouchMinHeight),
|
||||
@@ -1774,6 +1776,7 @@ private fun DiaryListScreen(
|
||||
slot.date,
|
||||
diaryTimeFieldToApi(slot.trainingStart),
|
||||
diaryTimeFieldToApi(slot.trainingEnd),
|
||||
excludeFromBilling = false,
|
||||
)
|
||||
quickCreateBusy = false
|
||||
if (id != null) {
|
||||
@@ -1922,6 +1925,20 @@ private fun DiaryListScreen(
|
||||
enabled = !diaryState.isLoading,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text("Nicht abrechnen")
|
||||
Switch(
|
||||
checked = newDiaryExcludeFromBilling,
|
||||
onCheckedChange = { newDiaryExcludeFromBilling = it },
|
||||
enabled = !diaryState.isLoading,
|
||||
)
|
||||
}
|
||||
diaryState.error?.let { err ->
|
||||
Text(
|
||||
err,
|
||||
@@ -1941,6 +1958,7 @@ private fun DiaryListScreen(
|
||||
newDiaryDateStr.trim(),
|
||||
diaryTimeFieldToApi(newDiaryStart),
|
||||
diaryTimeFieldToApi(newDiaryEnd),
|
||||
newDiaryExcludeFromBilling,
|
||||
)
|
||||
if (id != null) {
|
||||
showNewDateDialog = false
|
||||
@@ -2775,10 +2793,11 @@ private fun DiaryDetailScreen(
|
||||
initialDate = entry.date.take(10),
|
||||
initialStart = entry.trainingStart.orEmpty(),
|
||||
initialEnd = entry.trainingEnd.orEmpty(),
|
||||
initialExcludeFromBilling = entry.excludeFromBilling,
|
||||
submitLabel = tr("common.save", "Speichern"),
|
||||
onSubmit = { _, start, end ->
|
||||
onSubmit = { _, start, end, excludeFromBilling ->
|
||||
dependencies.applicationScope.launch {
|
||||
dependencies.diaryManager.updateTimes(clubId, entry.id, start, end)
|
||||
dependencies.diaryManager.updateTimes(clubId, entry.id, start, end, excludeFromBilling)
|
||||
showEdit = false
|
||||
}
|
||||
},
|
||||
@@ -6878,14 +6897,16 @@ private fun DeleteAccountDialog(
|
||||
@Composable
|
||||
private fun DiaryEditForm(
|
||||
submitLabel: String,
|
||||
onSubmit: (date: String, trainingStart: String?, trainingEnd: String?) -> Unit,
|
||||
onSubmit: (date: String, trainingStart: String?, trainingEnd: String?, excludeFromBilling: Boolean) -> Unit,
|
||||
initialDate: String = "",
|
||||
initialStart: String = "",
|
||||
initialEnd: String = "",
|
||||
initialExcludeFromBilling: Boolean = false,
|
||||
) {
|
||||
var date by rememberSaveable { mutableStateOf(initialDate) }
|
||||
var start by rememberSaveable { mutableStateOf(initialStart) }
|
||||
var end by rememberSaveable { mutableStateOf(initialEnd) }
|
||||
var excludeFromBilling by rememberSaveable { mutableStateOf(initialExcludeFromBilling) }
|
||||
var error by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
|
||||
Card(modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp), elevation = 2.dp) {
|
||||
@@ -6895,6 +6916,17 @@ private fun DiaryEditForm(
|
||||
OutlinedTextField(value = start, onValueChange = { start = it }, label = { Text("Start") }, modifier = Modifier.weight(1f), singleLine = true)
|
||||
OutlinedTextField(value = end, onValueChange = { end = it }, label = { Text("Ende") }, modifier = Modifier.weight(1f), singleLine = true)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text("Nicht abrechnen")
|
||||
Switch(
|
||||
checked = excludeFromBilling,
|
||||
onCheckedChange = { excludeFromBilling = it },
|
||||
)
|
||||
}
|
||||
ErrorText(error)
|
||||
Button(onClick = {
|
||||
if (!date.matches(Regex("\\d{4}-\\d{2}-\\d{2}"))) {
|
||||
@@ -6906,7 +6938,7 @@ private fun DiaryEditForm(
|
||||
return@Button
|
||||
}
|
||||
error = null
|
||||
onSubmit(date, start.takeIf { it.isNotBlank() }, end.takeIf { it.isNotBlank() })
|
||||
onSubmit(date, start.takeIf { it.isNotBlank() }, end.takeIf { it.isNotBlank() }, excludeFromBilling)
|
||||
}) {
|
||||
Text(submitLabel)
|
||||
}
|
||||
|
||||
@@ -197,6 +197,65 @@ private fun GlobalOrdersScreen(dependencies: AppDependencies, onBack: () -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
fun quickSaveFlags(orderId: Int, nextStatus: String? = null, nextPaidConfirmed: Boolean? = null) {
|
||||
val snapshot = rows.firstOrNull { it.order.id == orderId } ?: return
|
||||
val patchedStatus = nextStatus ?: snapshot.draftStatus
|
||||
val patchedPaidConfirmed = nextPaidConfirmed ?: snapshot.draftPaidConfirmed
|
||||
val memberId = snapshot.order.memberId ?: return
|
||||
val orderClubId = snapshot.order.clubId ?: return
|
||||
|
||||
rows = rows.map {
|
||||
if (it.order.id == orderId) {
|
||||
it.copy(
|
||||
draftStatus = patchedStatus,
|
||||
draftPaidConfirmed = patchedPaidConfirmed,
|
||||
)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
savingIds = savingIds + orderId
|
||||
runCatching {
|
||||
dependencies.memberOrdersApi.update(
|
||||
clubId = orderClubId,
|
||||
memberId = memberId,
|
||||
orderId = orderId,
|
||||
body = MemberOrderPatchBody(
|
||||
// Nur Status-Flags sofort speichern; restliche Felder bleiben Draft bis "Speichern".
|
||||
item = snapshot.order.item,
|
||||
status = patchedStatus,
|
||||
cost = normalizeAmount(snapshot.order.cost),
|
||||
paidAmount = normalizeAmount(snapshot.order.paidAmount),
|
||||
budget = normalizeAmount(snapshot.order.budget),
|
||||
paidConfirmed = patchedPaidConfirmed,
|
||||
),
|
||||
).order
|
||||
}.onSuccess { updated ->
|
||||
if (updated != null) {
|
||||
rows = rows.map { row ->
|
||||
if (row.order.id == orderId) {
|
||||
row.copy(
|
||||
order = updated,
|
||||
draftStatus = updated.status,
|
||||
draftPaidConfirmed = updated.paidConfirmed,
|
||||
)
|
||||
} else {
|
||||
row
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onFailure { e ->
|
||||
rows = rows.map {
|
||||
if (it.order.id == orderId) snapshot else it
|
||||
}
|
||||
err = e.message
|
||||
}
|
||||
savingIds = savingIds - orderId
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -338,8 +397,9 @@ private fun GlobalOrdersScreen(dependencies: AppDependencies, onBack: () -> Unit
|
||||
orderStatuses.forEach { (v, k) ->
|
||||
TextButton(
|
||||
onClick = {
|
||||
rows = rows.map { if (it.order.id == o.id) it.copy(draftStatus = v) else it }
|
||||
quickSaveFlags(orderId = o.id, nextStatus = v)
|
||||
},
|
||||
enabled = !savingIds.contains(o.id),
|
||||
) {
|
||||
Text(
|
||||
tr(k, v),
|
||||
@@ -373,8 +433,9 @@ private fun GlobalOrdersScreen(dependencies: AppDependencies, onBack: () -> Unit
|
||||
Switch(
|
||||
checked = row.draftPaidConfirmed,
|
||||
onCheckedChange = { v ->
|
||||
rows = rows.map { if (it.order.id == o.id) it.copy(draftPaidConfirmed = v) else it }
|
||||
quickSaveFlags(orderId = o.id, nextPaidConfirmed = v)
|
||||
},
|
||||
enabled = !savingIds.contains(o.id),
|
||||
)
|
||||
Text(tr("orders.paidConfirmed", "Bezahlt bestätigt"), modifier = Modifier.padding(start = 8.dp))
|
||||
}
|
||||
|
||||
@@ -83,15 +83,27 @@ class DiaryApi(
|
||||
client.http.delete("/api/diary-date-activities/group/$clubId/$groupActivityId")
|
||||
}
|
||||
|
||||
suspend fun createDate(clubId: Int, date: String, trainingStart: String?, trainingEnd: String?): DiaryDate {
|
||||
suspend fun createDate(
|
||||
clubId: Int,
|
||||
date: String,
|
||||
trainingStart: String?,
|
||||
trainingEnd: String?,
|
||||
excludeFromBilling: Boolean = false,
|
||||
): DiaryDate {
|
||||
return client.http.post("/api/diary/$clubId") {
|
||||
setBody(CreateDiaryDateRequest(date, trainingStart, trainingEnd))
|
||||
setBody(CreateDiaryDateRequest(date, trainingStart, trainingEnd, excludeFromBilling))
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun updateTimes(clubId: Int, dateId: Int, trainingStart: String?, trainingEnd: String?): DiaryDate {
|
||||
suspend fun updateTimes(
|
||||
clubId: Int,
|
||||
dateId: Int,
|
||||
trainingStart: String?,
|
||||
trainingEnd: String?,
|
||||
excludeFromBilling: Boolean? = null,
|
||||
): DiaryDate {
|
||||
return client.http.put("/api/diary/$clubId") {
|
||||
setBody(UpdateDiaryTimesRequest(dateId, trainingStart, trainingEnd))
|
||||
setBody(UpdateDiaryTimesRequest(dateId, trainingStart, trainingEnd, excludeFromBilling))
|
||||
}.body()
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ data class DiaryDate(
|
||||
val date: String,
|
||||
val trainingStart: String? = null,
|
||||
val trainingEnd: String? = null,
|
||||
val excludeFromBilling: Boolean = false,
|
||||
val diaryNotes: List<DiaryNote> = emptyList(),
|
||||
val diaryTags: List<DiaryTag> = emptyList(),
|
||||
)
|
||||
@@ -31,6 +32,7 @@ data class CreateDiaryDateRequest(
|
||||
val date: String,
|
||||
val trainingStart: String? = null,
|
||||
val trainingEnd: String? = null,
|
||||
val excludeFromBilling: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -38,6 +40,7 @@ data class UpdateDiaryTimesRequest(
|
||||
val dateId: Int,
|
||||
val trainingStart: String? = null,
|
||||
val trainingEnd: String? = null,
|
||||
val excludeFromBilling: Boolean? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -31,6 +31,7 @@ data class DiaryDate(
|
||||
val clubId: Int,
|
||||
val trainingStart: String? = null,
|
||||
val trainingEnd: String? = null,
|
||||
val excludeFromBilling: Boolean = false,
|
||||
val activities: List<DiaryActivity> = emptyList(),
|
||||
val participants: List<Participant> = emptyList()
|
||||
)
|
||||
|
||||
@@ -263,10 +263,16 @@ class DiaryManager(
|
||||
}
|
||||
|
||||
/** @return neue `diaryDateId` bei Erfolg, sonst `null` */
|
||||
suspend fun createDate(clubId: Int, date: String, trainingStart: String?, trainingEnd: String?): Int? {
|
||||
suspend fun createDate(
|
||||
clubId: Int,
|
||||
date: String,
|
||||
trainingStart: String?,
|
||||
trainingEnd: String?,
|
||||
excludeFromBilling: Boolean = false,
|
||||
): Int? {
|
||||
_state.value = _state.value.copy(isLoading = true, error = null)
|
||||
return try {
|
||||
val created = diaryApi.createDate(clubId, date, trainingStart, trainingEnd)
|
||||
val created = diaryApi.createDate(clubId, date, trainingStart, trainingEnd, excludeFromBilling)
|
||||
loadDates(clubId)
|
||||
created.id
|
||||
} catch (t: Throwable) {
|
||||
@@ -275,10 +281,16 @@ class DiaryManager(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateTimes(clubId: Int, dateId: Int, trainingStart: String?, trainingEnd: String?) {
|
||||
suspend fun updateTimes(
|
||||
clubId: Int,
|
||||
dateId: Int,
|
||||
trainingStart: String?,
|
||||
trainingEnd: String?,
|
||||
excludeFromBilling: Boolean? = null,
|
||||
) {
|
||||
_state.value = _state.value.copy(isLoading = true, error = null)
|
||||
try {
|
||||
diaryApi.updateTimes(clubId, dateId, trainingStart, trainingEnd)
|
||||
diaryApi.updateTimes(clubId, dateId, trainingStart, trainingEnd, excludeFromBilling)
|
||||
loadDates(clubId)
|
||||
} catch (t: Throwable) {
|
||||
_state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Zeiten konnten nicht gespeichert werden"))
|
||||
|
||||
Reference in New Issue
Block a user