Fixes for invoicing projects from react where custom values are not passed across to the invoice

This commit is contained in:
David Bomba 2025-08-26 20:33:52 +10:00
parent 799425478d
commit 2ed4beb6e2
4 changed files with 88 additions and 53 deletions

View File

@ -114,6 +114,31 @@ class StoreTaskRequest extends Request
$this->files->set('file', [$this->file('file')]); $this->files->set('file', [$this->file('file')]);
} }
if(isset($input['time_log']) &&is_string($input['time_log'])) {
$input['time_log'] = json_decode($input['time_log'], true);
}
if(isset($input['time_log']) && is_array($input['time_log'])) {
$time_logs = $input['time_log'];
foreach($time_logs as &$time_log) {
if (is_string($time_log)) {
continue; //catch if it isn't even a proper time log
}
$time_log[0] = intval($time_log[0]);
$time_log[1] = intval($time_log[1]);
$time_log[2] = strval($time_log[2] ?? '');
$time_log[3] = boolval($time_log[3] ?? true);
}
$input['time_log'] = json_encode($time_logs);
}
/* Ensure the project is related */ /* Ensure the project is related */
if (array_key_exists('project_id', $input) && isset($input['project_id'])) { if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first(); $project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();

View File

@ -131,6 +131,32 @@ class UpdateTaskRequest extends Request
$input['color'] = ''; $input['color'] = '';
} }
if(isset($input['time_log']) &&is_string($input['time_log'])) {
$input['time_log'] = json_decode($input['time_log'], true);
}
if(isset($input['time_log']) && is_array($input['time_log'])) {
$time_logs = $input['time_log'];
foreach($time_logs as &$time_log) {
if (is_string($time_log)) {
continue; //catch if it isn't even a proper time log
}
$time_log[0] = intval($time_log[0] ?? 0);
$time_log[1] = intval($time_log[1] ?? 0);
$time_log[2] = strval($time_log[2] ?? '');
$time_log[3] = boolval($time_log[3] ?? true);
}
$input['time_log'] = json_encode($time_logs);
}
if (isset($input['project_id']) && isset($input['client_id'])) { if (isset($input['project_id']) && isset($input['client_id'])) {
$search_project_with_client = Project::withTrashed()->where('id', $input['project_id'])->where('client_id', $input['client_id'])->company()->doesntExist(); $search_project_with_client = Project::withTrashed()->where('id', $input['project_id'])->where('client_id', $input['client_id'])->company()->doesntExist();

View File

@ -58,7 +58,10 @@ class ProjectRepository extends BaseRepository
$item->task_id = $task->hashed_id; $item->task_id = $task->hashed_id;
$item->tax_id = (string) Product::PRODUCT_TYPE_SERVICE; $item->tax_id = (string) Product::PRODUCT_TYPE_SERVICE;
$item->type_id = '2'; $item->type_id = '2';
$item->custom_value1 = $task->custom_value1;
$item->custom_value2 = $task->custom_value2;
$item->custom_value3 = $task->custom_value3;
$item->custom_value4 = $task->custom_value4;
$lines[] = $item; $lines[] = $item;
} }
@ -86,6 +89,10 @@ class ProjectRepository extends BaseRepository
$item->tax_id = (string) Product::PRODUCT_TYPE_PHYSICAL; $item->tax_id = (string) Product::PRODUCT_TYPE_PHYSICAL;
$item->expense_id = $expense->hashed_id; $item->expense_id = $expense->hashed_id;
$item->type_id = '1'; $item->type_id = '1';
$item->custom_value1 = $expense->custom_value1;
$item->custom_value2 = $expense->custom_value2;
$item->custom_value3 = $expense->custom_value3;
$item->custom_value4 = $expense->custom_value4;
$lines[] = $item; $lines[] = $item;
}); });

View File

@ -144,16 +144,15 @@ class TaskApiTest extends TestCase
$data = [ $data = [
'client_id' => $this->client->hashed_id, 'client_id' => $this->client->hashed_id,
'description' => 'Test Task', 'description' => 'Test Task',
'time_log' => '[[1731391977,1731399177,null,2342432],[1731399178,1731499177,null, 1231231]]', 'time_log' => '[[1731391977,1731399177,null,2342432],[1731399178,1731499177, null, 1231231]]',
]; ];
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data); ])->postJson("/api/v1/tasks", $data);
$response->assertStatus(422); $response->assertStatus(200);
} }
@ -486,6 +485,7 @@ class TaskApiTest extends TestCase
$response->assertStatus(200); $response->assertStatus(200);
nlog($response->json());
} }
public function testUserFilters() public function testUserFilters()
@ -612,8 +612,9 @@ class TaskApiTest extends TestCase
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data); ])->postJson("/api/v1/tasks", $data);
$response->assertStatus(422); $response->assertStatus(200);
$this->assertEquals('[]', $response->json()['data']['time_log']);
} }
public function testTaskClientRateSet() public function testTaskClientRateSet()
@ -657,11 +658,14 @@ class TaskApiTest extends TestCase
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data); ])->postJson("/api/v1/tasks", $data);
$response->assertStatus(422); $response->assertStatus(200);
$arr = $response->json(); $arr = $response->json();
$this->assertEquals('[]', $response->json()['data']['time_log']);
} }
public function testTaskProjectRateSet() public function testTaskProjectRateSet()
@ -753,9 +757,8 @@ class TaskApiTest extends TestCase
$this->assertIsArray($logs); $this->assertIsArray($logs);
} }
public function testStartStopSanity() public function testStartStopSanity()
{ {
@ -781,7 +784,8 @@ class TaskApiTest extends TestCase
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/tasks/{$task->hashed_id}?stop=true", $task->toArray()); ])->putJson("/api/v1/tasks/{$task->hashed_id}?stop=true", $task->toArray());
$response->assertStatus(422); // nlog($response->json());
$response->assertStatus(200);
$task->time_log = null; $task->time_log = null;
@ -1096,33 +1100,6 @@ class TaskApiTest extends TestCase
} }
// public function testTaskLocking()
// {
// $data = [
// 'timelog' => [[1,2],[3,4]],
// ];
// $response = $this->withHeaders([
// 'X-API-SECRET' => config('ninja.api_secret'),
// 'X-API-TOKEN' => $this->token,
// ])->post('/api/v1/tasks', $data);
// $arr = $response->json();
// $response->assertStatus(200);
// $response = $this->withHeaders([
// 'X-API-SECRET' => config('ninja.api_secret'),
// 'X-API-TOKEN' => $this->token,
// ])->putJson('/api/v1/tasks/' . $arr['data']['id'], $data);
// $arr = $response->json();
// $response->assertStatus(200);
// }
public function testTimeLogValidation() public function testTimeLogValidation()
@ -1136,7 +1113,9 @@ class TaskApiTest extends TestCase
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/tasks', $data); ])->postJson('/api/v1/tasks', $data);
$response->assertStatus(422); $response->assertStatus(200);
$this->assertEquals('[]', $response->json()['data']['time_log']);
} }
@ -1185,6 +1164,7 @@ class TaskApiTest extends TestCase
$response->assertStatus(422); $response->assertStatus(422);
} }
public function testTimeLogValidation4() public function testTimeLogValidation4()
@ -1255,22 +1235,21 @@ class TaskApiTest extends TestCase
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->put('/api/v1/tasks/'.$arr['data']['id'], $data); ])->putJson('/api/v1/tasks/'.$arr['data']['id'], $data);
$response->assertStatus(200); $response->assertStatus(200);
try { $response = $this->withHeaders([
$response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token,
'X-API-TOKEN' => $this->token, ])->postJson('/api/v1/tasks', $data);
])->post('/api/v1/tasks', $data);
$arr = $response->json(); $arr = $response->json();
} catch (ValidationException $e) {
$response->assertStatus(302);
}
$this->assertNotEmpty($arr['data']['number']); $response->assertStatus(422);
// $this->assertNotEmpty($arr['data']['number']);
} }
public function testTaskPostNoDefinedTaskNumber() public function testTaskPostNoDefinedTaskNumber()
@ -1295,15 +1274,13 @@ class TaskApiTest extends TestCase
'client_id' => $this->faker->firstName(), 'client_id' => $this->faker->firstName(),
]; ];
try {
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/tasks', $data); ])->postJson('/api/v1/tasks', $data);
$arr = $response->json(); $arr = $response->json();
} catch (ValidationException $e) { $response->assertStatus(422);
$response->assertStatus(302);
}
} }
public function testTaskPostWithActionStart() public function testTaskPostWithActionStart()