Merge branch 'develop'
This commit is contained in:
commit
33fd880727
@ -252,7 +252,7 @@ class EmployeePayRateWriteSerializer(
|
||||
expected_lock_version = DraftWriteSerializerMixin.expected_lock_version
|
||||
employee_id = serializers.PrimaryKeyRelatedField(
|
||||
source="employee",
|
||||
queryset=Employee.objects.filter(is_active=True),
|
||||
queryset=Employee.objects.all(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
@ -268,6 +268,12 @@ class PayrollAdminWorkspaceView(PayrollAdminAPIView):
|
||||
.prefetch_related("departments_links__department")
|
||||
.order_by("last_name", "first_name", "id")
|
||||
)
|
||||
rate_employees = (
|
||||
Employee.objects.all()
|
||||
.select_related("position")
|
||||
.prefetch_related("departments_links__department")
|
||||
.order_by("last_name", "first_name", "id")
|
||||
)
|
||||
components = PayrollComponent.objects.order_by("display_order", "code")
|
||||
|
||||
current_run = None
|
||||
@ -456,6 +462,10 @@ class PayrollAdminWorkspaceView(PayrollAdminAPIView):
|
||||
{
|
||||
"permissions": permissions_payload,
|
||||
"employees": PayrollEmployeeMiniSerializer(employees, many=True).data,
|
||||
"rate_employees": PayrollEmployeeMiniSerializer(
|
||||
rate_employees,
|
||||
many=True,
|
||||
).data,
|
||||
"components": PayrollComponentSerializer(components, many=True).data,
|
||||
"periods": PayrollPeriodSerializer(periods, many=True).data,
|
||||
"selected_period": (
|
||||
|
||||
@ -150,6 +150,10 @@ def test_workspace_contract_is_minimal_and_redacts_money_without_view_all(
|
||||
first_name="Мария",
|
||||
last_name="Расчётова",
|
||||
)
|
||||
inactive_employee = user_factory(
|
||||
email="workspace.dismissed@example.test",
|
||||
active=False,
|
||||
)
|
||||
grant(manager, "manage_payroll_inputs")
|
||||
response = auth_client_factory(manager).get(api_url("admin-workspace"))
|
||||
|
||||
@ -184,6 +188,8 @@ def test_workspace_contract_is_minimal_and_redacts_money_without_view_all(
|
||||
"position": None,
|
||||
"department": None,
|
||||
}
|
||||
assert inactive_employee.pk not in {item["id"] for item in body["employees"]}
|
||||
assert inactive_employee.pk in {item["id"] for item in body["rate_employees"]}
|
||||
|
||||
|
||||
def test_period_table_lists_all_employees_and_requires_view_all(
|
||||
@ -1120,7 +1126,8 @@ def test_period_and_rate_drafts_use_exact_optimistic_versions_and_maker_checker(
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
assert inactive_response.status_code == 400
|
||||
assert inactive_response.status_code == 201, inactive_response.content
|
||||
assert inactive_response.json()["employee_id"] == inactive_employee.pk
|
||||
|
||||
created = maker_client.post(
|
||||
rate_list_url,
|
||||
|
||||
2
frontend/next-env.d.ts
vendored
2
frontend/next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@ -61,7 +61,8 @@ const weekdays = [
|
||||
const defaultWorkdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];
|
||||
const matrixDateColumnWidth = 140;
|
||||
const matrixEmployeeColumnWidth = 160;
|
||||
const matrixModalHorizontalPadding = 96;
|
||||
const matrixModalMaxWidth = 1152;
|
||||
const matrixModalHorizontalPadding = 48;
|
||||
|
||||
type AttendancePeriodPreset = "month" | "custom";
|
||||
type EmployeeQuery = {
|
||||
@ -611,9 +612,10 @@ const DAYS_RU_FROM_INDEX: Record<number, string> = {
|
||||
};
|
||||
|
||||
function calculateMatrixEmployeesPerPage(width: number) {
|
||||
const modalWidth = Math.min(width * 0.95, matrixModalMaxWidth);
|
||||
const availableWidth = Math.max(
|
||||
0,
|
||||
width - matrixModalHorizontalPadding - matrixDateColumnWidth,
|
||||
modalWidth - matrixModalHorizontalPadding - matrixDateColumnWidth,
|
||||
);
|
||||
return Math.min(
|
||||
8,
|
||||
|
||||
@ -898,8 +898,8 @@ export function PayrollAdminWorkspace({
|
||||
)}
|
||||
|
||||
{periodModalOpen ? <PayrollPeriodFormModal isOpen period={editingPeriod} periods={workspace.periods} onClose={() => setPeriodModalOpen(false)} onSubmit={savePeriod} onSaved={async () => { setPeriodModalOpen(false); const createdPeriodId = createdPeriodIdRef.current; createdPeriodIdRef.current = null; if (createdPeriodId) { setNotice("Период создан и выбран."); await loadWorkspace(createdPeriodId, true); setTableVersion((value) => value + 1); } else { await refreshView("Параметры периода сохранены."); } }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && rateModalOpen ? <PayrollRateFormModal isOpen period={selectedPeriod} employees={workspace.employees} rate={editingRate} onClose={() => setRateModalOpen(false)} onSubmit={saveRate} onSaved={async () => { setRateModalOpen(false); await refreshView("Черновик ставки сохранён."); }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && bulkPayRateModalOpen ? <PayrollBulkPayRateModal isOpen period={selectedPeriod} employees={workspace.employees} rates={rates} onClose={() => setBulkPayRateModalOpen(false)} onSubmit={(payload) => apiClient.bulkCreatePayrollAdminPayRates(selectedPeriod.id, payload)} onSaved={async (result: PayrollBulkPayRateResult) => { setBulkPayRateModalOpen(false); const changed = result.summary.created_drafts + result.summary.updated_drafts + result.summary.created_revisions; const details = [result.summary.unchanged ? `без изменений: ${result.summary.unchanged}` : "", result.summary.skipped ? `пропущено: ${result.summary.skipped}` : ""].filter(Boolean).join(", "); await refreshView(`Ставка сохранена для ${changed} сотрудников${details ? ` (${details})` : ""}.`); }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && rateModalOpen ? <PayrollRateFormModal isOpen period={selectedPeriod} employees={workspace.rate_employees} rate={editingRate} onClose={() => setRateModalOpen(false)} onSubmit={saveRate} onSaved={async () => { setRateModalOpen(false); await refreshView("Черновик ставки сохранён."); }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && bulkPayRateModalOpen ? <PayrollBulkPayRateModal isOpen period={selectedPeriod} employees={workspace.rate_employees} rates={rates} onClose={() => setBulkPayRateModalOpen(false)} onSubmit={(payload) => apiClient.bulkCreatePayrollAdminPayRates(selectedPeriod.id, payload)} onSaved={async (result: PayrollBulkPayRateResult) => { setBulkPayRateModalOpen(false); const changed = result.summary.created_drafts + result.summary.updated_drafts + result.summary.created_revisions; const details = [result.summary.unchanged ? `без изменений: ${result.summary.unchanged}` : "", result.summary.skipped ? `пропущено: ${result.summary.skipped}` : ""].filter(Boolean).join(", "); await refreshView(`Ставка сохранена для ${changed} сотрудников${details ? ` (${details})` : ""}.`); }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && bulkPointRateModalOpen ? <PayrollBulkPointRateModal isOpen period={selectedPeriod} rates={rates} onClose={() => setBulkPointRateModalOpen(false)} onSubmit={(payload) => apiClient.bulkSetPayrollAdminPointRate(selectedPeriod.id, payload)} onSaved={async (result: PayrollBulkPointRateResult) => { setBulkPointRateModalOpen(false); const changed = result.summary.updated_drafts + result.summary.created_revisions; const details = [result.summary.unchanged ? `без изменений: ${result.summary.unchanged}` : "", result.summary.skipped ? `пропущено: ${result.summary.skipped}` : ""].filter(Boolean).join(", "); await refreshView(`Цена балла сверх нормы сохранена для ${changed} ставок${details ? ` (${details})` : ""}.`); }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && bulkTargetPointsModalOpen ? <PayrollBulkTargetPointsModal isOpen period={selectedPeriod} employees={workspace.employees} records={workRecords} onClose={() => setBulkTargetPointsModalOpen(false)} onSubmit={(payload) => apiClient.bulkSetPayrollAdminTargetPoints(selectedPeriod.id, payload)} onSaved={async (result: PayrollBulkTargetPointsResult) => { setBulkTargetPointsModalOpen(false); const changed = result.summary.created_drafts + result.summary.updated_drafts + result.summary.created_revisions; const details = [result.summary.unchanged ? `без изменений: ${result.summary.unchanged}` : "", result.summary.skipped ? `пропущено: ${result.summary.skipped}` : ""].filter(Boolean).join(", "); await refreshView(`Норма баллов сохранена для ${changed} сотрудников${details ? ` (${details})` : ""}.`); }} onStale={handleStale} /> : null}
|
||||
{selectedPeriod && workModalOpen ? <PayrollWorkRecordFormModal isOpen period={selectedPeriod} employees={workspace.employees} record={editingWork} onClose={() => setWorkModalOpen(false)} onSubmit={saveWork} onSaved={async () => { setWorkModalOpen(false); await refreshView("Черновик выработки сохранён."); }} onStale={handleStale} /> : null}
|
||||
|
||||
@ -194,6 +194,7 @@ export type PayrollCalculationBlocker = {
|
||||
export type PayrollAdminWorkspace = {
|
||||
permissions: PayrollAdminPermissions;
|
||||
employees: PayrollAdminEmployee[];
|
||||
rate_employees: PayrollAdminEmployee[];
|
||||
components: PayrollComponent[];
|
||||
periods: PayrollAdminPeriod[];
|
||||
selected_period: PayrollAdminPeriod | null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user