diff --git a/.idea/php.xml b/.idea/php.xml
index 1f9ad773bc..c4bca77530 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -1,5 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -288,4 +308,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/Domain/Tickets/Services/Tickets.php b/app/Domain/Tickets/Services/Tickets.php
index d06b32a4ee..9b17443576 100644
--- a/app/Domain/Tickets/Services/Tickets.php
+++ b/app/Domain/Tickets/Services/Tickets.php
@@ -1514,7 +1514,8 @@ public function quickAddTicket($params): array|bool
$notification->authorId = session('userdata.id') ?? -1;
$notification->message = $message;
- $this->projectService->notifyProjectUsers($notification);
+ // Disabled: Don't notify project users when a ticket is created
+ // $this->projectService->notifyProjectUsers($notification);
return $result;
}
@@ -1673,7 +1674,8 @@ public function addTicket($values): array|int|bool
$notification->authorId = session('userdata.id') ?? -1;
$notification->message = $message;
- $this->projectService->notifyProjectUsers($notification);
+ // Disabled: Don't notify project users when a ticket is created
+ // $this->projectService->notifyProjectUsers($notification);
return $addTicketResponse;
}
@@ -2954,10 +2956,11 @@ public function prepareTicketDates(&$values)
)->formatDateTimeForDb();
unset($values['timeFrom']);
} else {
+ // Use noon (12:00:00) instead of start of day to preserve date when converting to UTC
$values['editFrom'] = dtHelper()->parseUserDateTime(
$values['editFrom'],
'start'
- )->formatDateTimeForDb();
+ )->setTime(12, 0, 0)->formatDateTimeForDb();
}
}
} catch (\Exception $e) {
@@ -2979,10 +2982,11 @@ public function prepareTicketDates(&$values)
)->formatDateTimeForDb();
unset($values['timeTo']);
} else {
+ // Use noon (12:00:00) instead of end of day to preserve date when converting to UTC
$values['editTo'] = dtHelper()->parseUserDateTime(
$values['editTo'],
- 'end'
- )->formatDateTimeForDb();
+ 'start'
+ )->setTime(12, 0, 0)->formatDateTimeForDb();
}
}
diff --git a/app/Domain/Tickets/Templates/partials/ticketsubmenu.blade.php b/app/Domain/Tickets/Templates/partials/ticketsubmenu.blade.php
index cc15be36b1..d88bcc4fa3 100644
--- a/app/Domain/Tickets/Templates/partials/ticketsubmenu.blade.php
+++ b/app/Domain/Tickets/Templates/partials/ticketsubmenu.blade.php
@@ -5,7 +5,6 @@
])
@if ($login::userIsAtLeast(\Leantime\Domain\Auth\Models\Roles::$editor))
-
@endif
+
\ No newline at end of file
diff --git a/app/Domain/Tickets/Templates/showTicketModal.blade.php b/app/Domain/Tickets/Templates/showTicketModal.blade.php
index 71fc4ffc6d..7b3b90001d 100644
--- a/app/Domain/Tickets/Templates/showTicketModal.blade.php
+++ b/app/Domain/Tickets/Templates/showTicketModal.blade.php
@@ -44,7 +44,6 @@
__('links.move_todo'); ?>
__('links.delete_todo'); ?>
-
+
-
-
+
+
+ __('label.id'); ?>
__('label.date'); ?>
__('label.user'); ?>
__('label.type'); ?>
@@ -79,7 +80,7 @@
__('label.description'); ?>
-
+
__(strtolower($kindMap[$entry['kind']]));
}
- $hoursFormatted = format($entry['hours'])->decimal();
+ $hoursFormatted = format_hours($entry['hours']);
$description = $entry['description'] ?? '';
+ $entryId = $entry['id'] ?? '';
?>
+
+
+
+ #= htmlspecialchars($entryId, ENT_QUOTES, 'UTF-8') ?>
+
+
+ -
+
+
@@ -148,6 +159,31 @@
leantime.ticketsController.initTimeSheetChart(labels, d2, d3, "canvas")
+ // Initialize editTimeModal for timesheet entries
+ if (typeof leantime.timesheetsController !== 'undefined' && typeof leantime.timesheetsController.initEditTimeModal === 'function') {
+ leantime.timesheetsController.initEditTimeModal();
+ } else {
+ // Fallback: Initialize nyroModal directly if timesheetsController is not available
+ var canvasoptions = {
+ sizes: {
+ minW: 700,
+ minH: 1000,
+ },
+ resizable: true,
+ autoSizable: true,
+ callbacks: {
+ beforeShowCont: function () {
+ jQuery(".showDialogOnLoad").show();
+ },
+ beforeClose: function () {
+ location.reload();
+ }
+ },
+ titleFromIframe: true
+ };
+ jQuery(".editTimeModal").nyroModal(canvasoptions);
+ }
+
});
diff --git a/app/Domain/Timesheets/Controllers/EditTime.php b/app/Domain/Timesheets/Controllers/EditTime.php
index b37b1783e4..a5f729aa02 100644
--- a/app/Domain/Timesheets/Controllers/EditTime.php
+++ b/app/Domain/Timesheets/Controllers/EditTime.php
@@ -215,7 +215,7 @@ public function run(): Response
$this->tpl->assign('allTickets', $this->tickets->getAll());
$this->tpl->assign('kind', $this->timesheetsRepo->kind);
- return $this->tpl->display('timesheets.editTime');
+ return $this->tpl->displayPartial('timesheets.editTime');
} else {
return $this->tpl->displayPartial('errors.error403');
}
diff --git a/app/Domain/Timesheets/Controllers/ShowAll.php b/app/Domain/Timesheets/Controllers/ShowAll.php
index 4d91ea89c6..9b08f7edce 100644
--- a/app/Domain/Timesheets/Controllers/ShowAll.php
+++ b/app/Domain/Timesheets/Controllers/ShowAll.php
@@ -212,6 +212,15 @@ public function run(): Response
$clientId
));
+ // Pass user's hours format preference to template for CSV export
+ $userId = session('userdata.id');
+ $hoursFormat = 'decimal';
+ if ($userId) {
+ $settingsService = app()->make(\Leantime\Domain\Setting\Services\Setting::class);
+ $hoursFormat = $settingsService->getSetting('usersettings.'.$userId.'.hours_format', 'decimal');
+ }
+ $this->tpl->assign('hoursFormat', $hoursFormat);
+
return $this->tpl->display('timesheets.showAll');
}
}
diff --git a/app/Domain/Timesheets/Controllers/ShowMyList.php b/app/Domain/Timesheets/Controllers/ShowMyList.php
index c5e2b8cc23..a882c7f8fc 100644
--- a/app/Domain/Timesheets/Controllers/ShowMyList.php
+++ b/app/Domain/Timesheets/Controllers/ShowMyList.php
@@ -64,6 +64,15 @@ public function run(): Response
paid: -1
));
+ // Pass user's hours format preference to template for CSV export
+ $userId = session('userdata.id');
+ $hoursFormat = 'decimal';
+ if ($userId) {
+ $settingsService = app()->make(\Leantime\Domain\Setting\Services\Setting::class);
+ $hoursFormat = $settingsService->getSetting('usersettings.'.$userId.'.hours_format', 'decimal');
+ }
+ $this->tpl->assign('hoursFormat', $hoursFormat);
+
return $this->tpl->display('timesheets.showMyList');
}
}
diff --git a/app/Domain/Timesheets/Js/timesheetsExportFormatter.js b/app/Domain/Timesheets/Js/timesheetsExportFormatter.js
new file mode 100644
index 0000000000..0d0737142f
--- /dev/null
+++ b/app/Domain/Timesheets/Js/timesheetsExportFormatter.js
@@ -0,0 +1,40 @@
+/**
+ * Timesheets export helper
+ *
+ * Provides a single place to resolve CSV export values while
+ * keeping the original markup untouched (aside from data attributes).
+ */
+(function () {
+ window.leantime = window.leantime || {};
+ window.leantime.timesheetsExport = window.leantime.timesheetsExport || {};
+
+ /**
+ * Resolve the value that should be exported for a cell.
+ *
+ * @param {jQuery} $node
+ * @param {string} fallbackData
+ * @returns {string}
+ */
+ window.leantime.timesheetsExport.resolveCell = function ($node, fallbackData) {
+ if (typeof $node.data('order') === 'undefined') {
+ return fallbackData;
+ }
+
+ if (! $node.hasClass('js-timesheet-hours')) {
+ return $node.data('order');
+ }
+
+ var tableFormat = ($node.closest('table[data-hours-format]').data('hours-format') || '').toString();
+
+ if (tableFormat === 'human') {
+ if (typeof $node.data('export-display') !== 'undefined') {
+ return $node.data('export-display');
+ }
+
+ return $node.text().trim();
+ }
+
+ return $node.data('order');
+ };
+})();
+
diff --git a/app/Domain/Timesheets/Repositories/Timesheets.php b/app/Domain/Timesheets/Repositories/Timesheets.php
index 187b978afb..bd71f7e4dd 100644
--- a/app/Domain/Timesheets/Repositories/Timesheets.php
+++ b/app/Domain/Timesheets/Repositories/Timesheets.php
@@ -665,7 +665,7 @@ public function addTime(array $values): void
$call = $this->dbcall(func_get_args());
$call->prepare($query);
-
+
$workDateTime = new \DateTime($values['date']);
$currentTime = new \DateTime('now');
@@ -676,7 +676,7 @@ public function addTime(array $values): void
$workDateTime->setTime($currentHours, $currentMinutes, $currentSeconds);
$combinedWorkDate = $workDateTime->format('Y-m-d H:i:s');
-
+
$call->bindValue(':userId', $values['userId']);
$call->bindValue(':ticket', $values['ticket']);
$call->bindValue(':loggingDate', $combinedWorkDate);
@@ -775,7 +775,7 @@ public function punchOut(int $ticketId): float|false|int
$call->bindValue(':workDate', $formattedDate);
-
+
$query = "INSERT INTO `zp_timesheets` (userId, ticketId, workDate, hours, kind, modified)
VALUES (:sessionId, :ticketId, :workDate, :hoursWorked, 'GENERAL_BILLABLE', :modified)
ON DUPLICATE KEY UPDATE hours = hours + :hoursWorked";
@@ -864,6 +864,29 @@ public function upsertTimesheetEntry(array $values): void
*/
public function updateTime(array $values): void
{
+ // Get the original timesheet to preserve the time component
+ $originalTimesheet = $this->getTimesheet($values['id']);
+
+ // If we have an original timesheet, preserve its time component
+ if ($originalTimesheet && !empty($originalTimesheet['workDate'])) {
+ $originalDate = new Carbon($originalTimesheet['workDate'], 'UTC');
+
+ if (is_string($values['date'])) {
+ $newDate = new Carbon($values['date'], 'UTC');
+ } else {
+ $newDate = $values['date'];
+ }
+
+ // Preserve original time (hour, minute, second) with new date
+ $preservedDateTime = $newDate->setTime(
+ $originalDate->hour,
+ $originalDate->minute,
+ $originalDate->second
+ );
+
+ $values['date'] = $preservedDateTime->format('Y-m-d H:i:s');
+ }
+
$query = 'UPDATE
zp_timesheets
SET
diff --git a/app/Domain/Timesheets/Templates/editTime.tpl.php b/app/Domain/Timesheets/Templates/editTime.tpl.php
index e9f68a777c..dcc61059d4 100644
--- a/app/Domain/Timesheets/Templates/editTime.tpl.php
+++ b/app/Domain/Timesheets/Templates/editTime.tpl.php
@@ -54,11 +54,10 @@
displayNotification() ?>
-
-
+
-
-
-
-
+
\ No newline at end of file
diff --git a/app/Domain/Timesheets/Templates/showAll.tpl.php b/app/Domain/Timesheets/Templates/showAll.tpl.php
index 677a316f70..2484ad936d 100644
--- a/app/Domain/Timesheets/Templates/showAll.tpl.php
+++ b/app/Domain/Timesheets/Templates/showAll.tpl.php
@@ -4,8 +4,38 @@
foreach ($__data as $var => $val) {
$$var = $val; // necessary for blade refactor
}
+$hoursFormat = session('usersettings.hours_format', 'decimal');
?>
@@ -342,7 +345,7 @@ class="hourCell"
/>
-
+
diff --git a/app/Domain/Timesheets/Templates/showMyList.tpl.php b/app/Domain/Timesheets/Templates/showMyList.tpl.php
index 20ac115dcd..4791ab2ef9 100644
--- a/app/Domain/Timesheets/Templates/showMyList.tpl.php
+++ b/app/Domain/Timesheets/Templates/showMyList.tpl.php
@@ -6,6 +6,7 @@
foreach ($__data as $var => $val) {
$$var = $val; // necessary for blade refactor
}
+$hoursFormat = session('usersettings.hours_format', 'decimal');
?>
@@ -84,7 +85,7 @@ class="dateTo"
-
+
@@ -133,15 +134,18 @@ class="dateTo"
date(); ?>
time(); ?>
-
- e($row['hours'] ?: 0); ?>
+ e($row['hours']); ?>">e($row['hours'] ?: 0); ?> */ ?>
+
+
-
- e($row['planHours'] ?: 0); ?>
+ e($row['planHours']); ?>">e($row['planHours'] ?: 0); ?> */ ?>
+
+
-
-
+ "> */ ?>
+
+
e($row['headline']); ?>
@@ -193,7 +197,8 @@ class="dateTo"
__('label.total_hours')?>
-
+ */ ?>
+
@@ -202,6 +207,35 @@ class="dateTo"