Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow creating draft attendance requests for future dates #2255

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a074393
allow marking attendance request for future dates
iamejaaz Oct 5, 2024
74c29a4
fix: Turkish translations
frappe-pr-bot Oct 1, 2024
1dcfff5
fix: Persian translations
frappe-pr-bot Oct 1, 2024
471d98c
fix: Turkish translations
frappe-pr-bot Oct 2, 2024
4c9a8c8
fix: Persian translations
frappe-pr-bot Oct 2, 2024
e0227e3
fix: Arabic translations
frappe-pr-bot Oct 2, 2024
a7fc14a
fix: French translations
frappe-pr-bot Oct 2, 2024
eae68b4
fix: Spanish translations
frappe-pr-bot Oct 2, 2024
d7ef276
fix: German translations
frappe-pr-bot Oct 2, 2024
26c1c19
fix: Swedish translations
frappe-pr-bot Oct 2, 2024
b3a70f2
fix: Bosnian translations
frappe-pr-bot Oct 2, 2024
8abb04d
fix: Esperanto translations
frappe-pr-bot Oct 2, 2024
f6bb1e2
chore: update POT file
frappe-pr-bot Oct 6, 2024
60b35d0
feat: add Attendance Dashboard
krantheman Aug 9, 2024
72789f9
feat: add attendance summary
krantheman Aug 9, 2024
51a3042
feat: fetch event of selected month
krantheman Aug 9, 2024
67da8ea
feat: add calendar days
krantheman Aug 20, 2024
fc30070
chore: remove redundant classes
krantheman Aug 20, 2024
840444c
fix: colors and icons
krantheman Aug 21, 2024
49d26b6
feat: add Upcoming Shifts list
krantheman Aug 22, 2024
78da0a7
feat: add color coding to summary
krantheman Aug 22, 2024
eec77d8
feat: add ionic modal for shifts
krantheman Aug 22, 2024
b796590
feat: add Shift Request list
krantheman Aug 26, 2024
cbecd52
feat: add Shift Request list view
krantheman Aug 26, 2024
8f35218
feat: add Shift Request form
krantheman Aug 26, 2024
9e9953f
feat: set shift request approvers
krantheman Aug 27, 2024
731c723
feat: add recent attendance requests
krantheman Aug 27, 2024
c7e678a
feat: add Attendance Request list and form
krantheman Aug 27, 2024
accee49
feat: add Attendance Request summary
krantheman Aug 27, 2024
bba9de0
fix: submit for Attendance Request
krantheman Sep 9, 2024
1d3a99e
refactor: upcoming shifts
krantheman Sep 9, 2024
498262c
feat: add Shift Assignment list
krantheman Sep 9, 2024
e849554
feat: add Shift Assignment form
krantheman Sep 10, 2024
03de89a
fix: render 'New' button only if user has perms for doctype
krantheman Sep 10, 2024
97c9891
feat: add Employee Checkin list
krantheman Sep 10, 2024
943fbf3
feat: add Employee Checkin form
krantheman Sep 10, 2024
0b29039
feat: add DatetimePicker and switch from Input to DatePicker
krantheman Sep 11, 2024
b298aec
feat: add Employee Checkin summary
krantheman Sep 12, 2024
44c0dda
feat: add Request a Shift button
krantheman Sep 17, 2024
1401771
feat: show employee name for team requests
krantheman Sep 17, 2024
3e16845
refactor: remove tabs where unnecessary
krantheman Sep 17, 2024
71e8c24
fix: Employee Checkin list filters
krantheman Sep 17, 2024
1074c9b
feat: add attendance and shift requests to home page
krantheman Sep 17, 2024
2fc11f1
feat: show team shift requests
krantheman Sep 18, 2024
f4de147
feat: update icons for attendance and shifts
krantheman Sep 18, 2024
4947937
feat: add notifications for shift request
krantheman Sep 18, 2024
4a35edd
refactor: use ListItem component for all items
krantheman Sep 18, 2024
78d72a6
fix: get_shift_requests api fields
krantheman Sep 19, 2024
45bf48d
fix(Salary Slip): consider only leaves taken until End Date in Leave …
krantheman Oct 8, 2024
b32d668
test(Salary Slip): leave details
krantheman Oct 8, 2024
f8536f0
refactor: change parameter name
iamejaaz Oct 15, 2024
ce255f7
refactor: remove unwanted space
iamejaaz Oct 15, 2024
ce42ed1
refactor: Employee working on holiday
niraj2477 Oct 17, 2023
4e04566
fix: Update condition in query builder
niraj2477 Oct 17, 2023
621ecac
chore: update site urls from frappehr.com to frappe.io/hr
krantheman Oct 9, 2024
2b9e380
feat(Expense Claim): allow update of cost centre and project on submit
krantheman Oct 9, 2024
be7377f
feat: create Shift Location doctype
krantheman Jul 4, 2024
b1566a1
feat(Shift & Attendance): add Shift Location link
krantheman Oct 9, 2024
9cf3fc2
refactor: show only holidays from employee's holidays
krantheman Oct 14, 2024
98976eb
refactor: column widths
krantheman Oct 14, 2024
cccc35c
perf: improve get_holidays function
krantheman Oct 14, 2024
d2cf834
fix(Leave Policy Assignment): calculation of months passed
krantheman Jan 8, 2024
3336111
refactor: use IntegrationTestCase instead of FrappeTestCase
krantheman Oct 10, 2024
a665b18
fix: payroll bank entry with the Loan Repayment amount
Nihantra-Patel Oct 15, 2024
94107a8
fix: payroll bank entry with the Loan Repayment amount
Nihantra-Patel Oct 15, 2024
f85c77a
fix: payroll bank entry with the Loan Repayment amount
Nihantra-Patel Oct 15, 2024
07dd28c
fix: payroll bank entry with the Loan Repayment amount
Nihantra-Patel Oct 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ body:
Welcome to Frappe HR issue tracker! Before submitting a request, please consider the following:
1. This tracker should only be used to report bugs and request features / enhancements to Frappe HR
- For questions and general support, checkout the [documentation](https://frappehr.com/docs) or use the [forum](https://discuss.frappe.io) to get inputs from the open source community.
- For questions and general support, checkout the [documentation](https://docs.frappe.io/hr) or use the [forum](https://discuss.frappe.io) to get inputs from the open source community.
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
Expand Down Expand Up @@ -47,4 +47,4 @@ body:
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
...
...
2 changes: 1 addition & 1 deletion .github/helper/documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def docs_link_exists(body):
parts = parsed_url.path.split("/")
if len(parts) == 5 and parts[1] == "frappe" and parts[2] == "hrms":
return True
elif parsed_url.netloc == "frappehr.com":
elif parsed_url.netloc == "docs.frappe.io":
return True


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Frappe HR has everything you need to drive excellence within the company. It's a

## Learning and Community

1. [Documentation](https://frappehr.com/docs) - Extensive documentation for Frappe HR.
1. [Documentation](https://docs.frappe.io/hr) - Extensive documentation for Frappe HR.
2. [User Forum](https://discuss.erpnext.com/) - Engage with the community of ERPNext users and service providers.
3. [Telegram Group](https://t.me/frappehr) - Get instant help from the community of users.

Expand Down
122 changes: 122 additions & 0 deletions frontend/src/components/AttendanceCalendar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<template>
<div class="flex flex-col w-full gap-5" v-if="calendarEvents.data">
<div class="text-lg text-gray-800 font-bold">Attendance Calendar</div>

<div class="flex flex-col gap-6 bg-white py-6 px-3.5 rounded-lg border-none">
<!-- Month Change -->
<div class="flex flex-row justify-between items-center px-4">
<Button
icon="chevron-left"
variant="ghost"
@click="firstOfMonth = firstOfMonth.subtract(1, 'M')"
/>
<span class="text-lg text-gray-800 font-bold">
{{ firstOfMonth.format("MMMM") }} {{ firstOfMonth.format("YYYY") }}
</span>
<Button
icon="chevron-right"
variant="ghost"
@click="firstOfMonth = firstOfMonth.add(1, 'M')"
/>
</div>

<!-- Calendar -->
<div class="grid grid-cols-7 gap-y-3">
<div
v-for="day in DAYS"
class="flex justify-center text-gray-600 text-sm font-medium leading-6"
>
{{ day }}
</div>
<div v-for="_ in firstOfMonth.get('d')" />
<div v-for="index in firstOfMonth.endOf('M').get('D')">
<div
class="h-8 w-8 flex rounded-full mx-auto"
:class="getEventOnDate(index) && `bg-${colorMap[getEventOnDate(index)]}`"
>
<span class="text-gray-800 text-sm font-medium m-auto">
{{ index }}
</span>
</div>
</div>
</div>

<hr />

<!-- Summary -->
<div class="grid grid-cols-4 mx-2">
<div v-for="status in summaryStatuses" class="flex flex-col gap-1">
<div class="flex flex-row gap-1 items-center">
<span class="rounded full h-3 w-3" :class="`bg-${colorMap[status]}`" />
<span class="text-gray-600 text-sm font-medium leading-5"> {{ status }} </span>
</div>
<span class="text-gray-800 text-base font-semibold leading-6 mx-auto">
{{ summary[status] || 0 }}
</span>
</div>
</div>
</div>
</div>
</template>

<script setup>
import { computed, inject, ref, watch } from "vue"
import { createResource } from "frappe-ui"
const dayjs = inject("$dayjs")
const employee = inject("$employee")
const firstOfMonth = ref(dayjs().date(1).startOf("D"))
const colorMap = {
Present: "green-200",
"Work From Home": "green-200",
"Half Day": "yellow-100",
Absent: "red-100",
"On Leave": "blue-100",
Holiday: "gray-100",
}
const summaryStatuses = ["Present", "Half Day", "Absent", "On Leave"]
const summary = computed(() => {
const summary = {}
for (const status of Object.values(calendarEvents.data)) {
let updatedStatus = status === "Work From Home" ? "Present" : status
if (updatedStatus in summary) {
summary[updatedStatus] += 1
} else {
summary[updatedStatus] = 1
}
}
return summary
})
watch(
() => firstOfMonth.value,
() => {
calendarEvents.fetch()
}
)
const getEventOnDate = (date) => {
return calendarEvents.data[firstOfMonth.value.date(date).format("YYYY-MM-DD")]
}
const DAYS = ["S", "M", "T", "W", "T", "F", "S"]
//resources
const calendarEvents = createResource({
url: "hrms.api.get_attendance_calendar_events",
auto: true,
cache: "hrms:attendance_calendar_events",
makeParams() {
return {
employee: employee.data.name,
from_date: firstOfMonth.value.format("YYYY-MM-DD"),
to_date: firstOfMonth.value.endOf("M").format("YYYY-MM-DD"),
}
},
})
</script>
54 changes: 54 additions & 0 deletions frontend/src/components/AttendanceRequestItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<template>
<ListItem>
<template #left>
<AttendanceIcon class="h-5 w-5 text-gray-500" />
<div class="flex flex-col items-start gap-1.5">
<div class="text-base font-normal text-gray-800">
{{ props.doc.reason }}
</div>
<div class="text-xs font-normal text-gray-500">
<span>{{ props.doc.attendance_dates || getDates(props.doc) }}</span>
<span v-if="props.doc.to_date">
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">{{
`${props.doc.total_attendance_days || getTotalDays(props.doc)}d`
}}</span>
</span>
</div>
</div>
</template>
<template #right>
<Badge variant="outline" :theme="colorMap[status]" :label="status" size="md" />
<FeatherIcon name="chevron-right" class="h-5 w-5 text-gray-500" />
</template>
</ListItem>
</template>

<script setup>
import { computed } from "vue"
import { Badge, FeatherIcon } from "frappe-ui"
import ListItem from "@/components/ListItem.vue"
import AttendanceIcon from "@/components/icons/AttendanceIcon.vue"
import { getDates, getTotalDays } from "@/data/attendance"
const props = defineProps({
doc: {
type: Object,
},
workflowStateField: {
type: String,
required: false,
},
})
const status = computed(() => {
if (props.workflowStateField) return props.doc[props.workflowStateField]
return props.doc.docstatus ? "Submitted" : "Draft"
})
const colorMap = {
Draft: "gray",
Submitted: "blue",
}
</script>
6 changes: 6 additions & 0 deletions frontend/src/components/BottomTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import HomeIcon from "@/components/icons/HomeIcon.vue"
import LeaveIcon from "@/components/icons/LeaveIcon.vue"
import ExpenseIcon from "@/components/icons/ExpenseIcon.vue"
import SalaryIcon from "@/components/icons/SalaryIcon.vue"
import AttendanceIcon from "@/components/icons/AttendanceIcon.vue"

const route = useRoute()

Expand All @@ -39,6 +40,11 @@ const tabItems = [
title: "Home",
route: "/home",
},
{
icon: AttendanceIcon,
title: "Attendance",
route: "/dashboard/attendance",
},
{
icon: LeaveIcon,
title: "Leaves",
Expand Down
20 changes: 7 additions & 13 deletions frontend/src/components/CheckInPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

<template v-if="settings.data?.allow_employee_checkin_from_mobile_app">
<div class="font-medium text-sm text-gray-500 mt-1.5" v-if="lastLog">
Last {{ lastLogType }} was at {{ lastLogTime }}
<span>Last {{ lastLogType }} was at {{ formatTimestamp(lastLog.time) }}</span>
<span class="whitespace-pre"> &middot; </span>
<router-link :to="{ name: 'EmployeeCheckinListView' }" v-slot="{ navigate }">
<span @click="navigate" class="underline">View List</span>
</router-link>
</div>
<Button
class="mt-4 mb-1 drop-shadow-sm py-5 text-base"
Expand Down Expand Up @@ -75,6 +79,8 @@ import { createResource, createListResource, toast, FeatherIcon } from "frappe-u
import { computed, inject, ref, onMounted, onBeforeUnmount } from "vue"
import { IonModal, modalController } from "@ionic/vue"

import { formatTimestamp } from "@/utils/formatters"

const DOCTYPE = "Employee Checkin"

const socket = inject("$socket")
Expand Down Expand Up @@ -115,18 +121,6 @@ const nextAction = computed(() => {
: { action: "IN", label: "Check In" }
})

const lastLogTime = computed(() => {
const timestamp = lastLog?.value?.time
const formattedTime = dayjs(timestamp).format("hh:mm a")

if (dayjs(timestamp).isToday()) return formattedTime
else if (dayjs(timestamp).isYesterday()) return `${formattedTime} yesterday`
else if (dayjs(timestamp).isSame(dayjs(), "year"))
return `${formattedTime} on ${dayjs(timestamp).format("D MMM")}`

return `${formattedTime} on ${dayjs(timestamp).format("D MMM, YYYY")}`
})

function handleLocationSuccess(position) {
latitude.value = position.coords.latitude
longitude.value = position.coords.longitude
Expand Down
83 changes: 33 additions & 50 deletions frontend/src/components/EmployeeAdvanceItem.vue
Original file line number Diff line number Diff line change
@@ -1,52 +1,37 @@
<template>
<div class="flex flex-col w-full justify-center gap-2.5">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-row items-start gap-3 grow">
<EmployeeAdvanceIcon class="h-5 w-5 mt-[3px] text-gray-500" />
<div class="flex flex-col items-start gap-1">
<div
v-if="props.doc.balance_amount"
class="text-lg font-bold text-gray-800 leading-6"
>
{{ `${currency} ${props.doc.balance_amount} /` }}
<span class="text-gray-600">
{{ `${currency} ${props.doc.paid_amount}` }}
</span>
</div>
<div v-else class="text-lg font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.advance_amount}` }}
</div>
<div class="text-xs font-normal text-gray-500">
<span>
{{ props.doc.purpose }}
</span>
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">
{{ postingDate }}
</span>
</div>
<ListItem
:isTeamRequest="props.isTeamRequest"
:employee="props.doc.employee"
:employeeName="props.doc.employee_name"
>
<template #left>
<EmployeeAdvanceIcon class="h-5 w-5 mt-[3px] text-gray-500" />
<div class="flex flex-col items-start gap-1">
<div v-if="props.doc.balance_amount" class="text-lg font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.balance_amount} /` }}
<span class="text-gray-600">
{{ `${currency} ${props.doc.paid_amount}` }}
</span>
</div>
<div v-else class="text-lg font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.advance_amount}` }}
</div>
<div class="text-xs font-normal text-gray-500">
<span>
{{ props.doc.purpose }}
</span>
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">
{{ postingDate }}
</span>
</div>
</div>
<div class="flex flex-row justify-end items-center gap-2">
<Badge
variant="outline"
:theme="colorMap[status]"
:label="status"
size="md"
/>
<FeatherIcon name="chevron-right" class="h-5 w-5 text-gray-500" />
</div>
</div>
<div
v-if="props.isTeamRequest"
class="flex flex-row items-center gap-2 pl-8"
>
<EmployeeAvatar :employeeID="props.doc.employee" />
<div class="text-sm text-gray-600 grow">
{{ props.doc.employee_name }}
</div>
</div>
</div>
</template>
<template #right>
<Badge variant="outline" :theme="colorMap[status]" :label="status" size="md" />
<FeatherIcon name="chevron-right" class="h-5 w-5 text-gray-500" />
</template>
</ListItem>
</template>

<script setup>
Expand All @@ -55,7 +40,7 @@ import { computed, inject } from "vue"
import { getCurrencySymbol } from "@/data/currencies"
import EmployeeAvatar from "@/components/EmployeeAvatar.vue"
import ListItem from "@/components/ListItem.vue"
import EmployeeAdvanceIcon from "@/components/icons/EmployeeAdvanceIcon.vue"
const dayjs = inject("$dayjs")
Expand Down Expand Up @@ -88,8 +73,6 @@ const postingDate = computed(() => {
})
const status = computed(() => {
return props.workflowStateField
? props.doc[props.workflowStateField]
: props.doc.status
return props.workflowStateField ? props.doc[props.workflowStateField] : props.doc.status
})
</script>
Loading