jobs_update.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <?php
  2. require_once __DIR__ . '/../lib/identity.php';
  3. require_once __DIR__ . '/../lib/jobs.php';
  4. [$actor, $vendor_id] = resolve_request_actor();
  5. $pdo = db();
  6. $job_id = (int) ($_POST['job_id'] ?? 0);
  7. $action = $_POST['action'] ?? '';
  8. $column = $_POST['column'] ?? '';
  9. $value = $_POST['value'] ?? '';
  10. if ($job_id <= 0) {
  11. http_response_code(400);
  12. echo 'Bad job_id';
  13. return;
  14. }
  15. $stmt = $pdo->prepare('SELECT * FROM jobs WHERE id = ?');
  16. $stmt->execute([$job_id]);
  17. $job = $stmt->fetch();
  18. if (!$job) {
  19. http_response_code(404);
  20. echo 'Job not found';
  21. return;
  22. }
  23. if (!empty($job['deleted_at'])) {
  24. http_response_code(410);
  25. echo 'Job has been deleted';
  26. return;
  27. }
  28. // Vendor-side requests are scoped to their own jobs.
  29. if ($actor !== 'ICG' && (int) $job['vendor_id'] !== $vendor_id) {
  30. http_response_code(403);
  31. echo 'Wrong vendor';
  32. return;
  33. }
  34. // --- Button-driven state transitions ---
  35. // Undo is dynamic: it reverses one step based on current status. Vendors only.
  36. // Once ICG marks Received, undo is no longer available (vendor would need to
  37. // message ICG to get them to back it out).
  38. if ($action === 'undo') {
  39. if ($actor === 'ICG') {
  40. http_response_code(403);
  41. echo 'Undo is a vendor action';
  42. return;
  43. }
  44. if ($job['status'] === 'Finished') {
  45. apply_job_change($job, 'status', '', $actor);
  46. } elseif ($job['status'] === 'Shipped') {
  47. apply_job_change($job, 'status', 'Finished', $actor);
  48. } else {
  49. http_response_code(400);
  50. echo 'Nothing to undo from current status';
  51. return;
  52. }
  53. echo 'Success';
  54. return;
  55. }
  56. if ($action !== '') {
  57. $allowed = [
  58. 'acknowledge' => ['ack', '', ['vendor']],
  59. 'mark_finished' => ['status', 'Finished', ['vendor']],
  60. 'mark_shipped' => ['status', 'Shipped', ['vendor']],
  61. 'mark_received' => ['status', 'Received', ['ICG']],
  62. ];
  63. if (!isset($allowed[$action])) {
  64. http_response_code(400);
  65. echo 'Unknown action';
  66. return;
  67. }
  68. [$col, $new, $roles] = $allowed[$action];
  69. $role = $actor === 'ICG' ? 'ICG' : 'vendor';
  70. if (!in_array($role, $roles, true)) {
  71. http_response_code(403);
  72. echo 'Action not allowed for this role';
  73. return;
  74. }
  75. apply_job_change($job, $col, $new, $actor);
  76. echo 'Success';
  77. return;
  78. }
  79. // --- Field edits (ICG only) ---
  80. if ($actor !== 'ICG') {
  81. http_response_code(403);
  82. echo 'Edits restricted to ICG';
  83. return;
  84. }
  85. $editable = ['job', 'material', 'description', 'qty', 'due_date'];
  86. if (!in_array($column, $editable, true)) {
  87. http_response_code(400);
  88. echo 'Unknown column';
  89. return;
  90. }
  91. $value = trim($value);
  92. if ($column === 'qty') {
  93. if ($value === '' || !is_numeric($value)) {
  94. http_response_code(400);
  95. echo 'Qty must be a number';
  96. return;
  97. }
  98. $value = (string) (int) $value;
  99. } elseif ($column === 'due_date') {
  100. $value = parse_due_date($value);
  101. }
  102. apply_job_change($job, $column, $value, $actor);
  103. // An ICG edit to a content field bumps an already-acknowledged row back to
  104. // 'changed' so the vendor sees it at the top of their list. We leave 'new'
  105. // alone — the row still needs acknowledgement, just for a different reason.
  106. if ($job['ack'] === '') {
  107. apply_job_change($job, 'ack', 'changed', $actor);
  108. }
  109. echo 'Success';