mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-19 21:52:15 +01:00
Compare commits
2 Commits
25f30fa820
...
feature/ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44b04a9b62 | ||
|
|
445e62d2a2 |
32
.dockerignore
Normal file
32
.dockerignore
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.git
|
||||||
|
.gitmodules
|
||||||
|
.github
|
||||||
|
.DS_Store
|
||||||
|
.fleet
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.log
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
.phpunit.cache
|
||||||
|
.phpunit.result.cache
|
||||||
|
coverage
|
||||||
|
test-results
|
||||||
|
playwright-report
|
||||||
|
blob-report
|
||||||
|
playwright/.cache
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
extensions/*/node_modules
|
||||||
|
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
auth.json
|
||||||
|
.env.backup
|
||||||
|
.rnd
|
||||||
|
|
||||||
|
_ide_helper.php
|
||||||
|
.phpstorm.meta.php
|
||||||
|
|
||||||
|
storage/logs/*
|
||||||
12
.github/workflows/build-onpremise.yml
vendored
12
.github/workflows/build-onpremise.yml
vendored
@@ -8,6 +8,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-onpremise.yml'
|
- '.github/workflows/build-onpremise.yml'
|
||||||
|
- 'extensions/manifest.json'
|
||||||
- 'docker/prod/**'
|
- 'docker/prod/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -95,10 +96,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
node-version: '20.x'
|
||||||
|
|
||||||
|
- name: "Read extension manifest"
|
||||||
|
id: extension-manifest
|
||||||
|
run: |
|
||||||
|
{
|
||||||
|
echo "invoicing_repository=$(jq -r '.Invoicing.repository' extensions/manifest.json)"
|
||||||
|
echo "invoicing_ref=$(jq -r '.Invoicing.ref' extensions/manifest.json)"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: "Checkout invoicing extension"
|
- name: "Checkout invoicing extension"
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: solidtime-io/extension-invoicing
|
repository: ${{ steps.extension-manifest.outputs.invoicing_repository }}
|
||||||
|
ref: ${{ steps.extension-manifest.outputs.invoicing_ref }}
|
||||||
path: extensions/Invoicing
|
path: extensions/Invoicing
|
||||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_INVOICING_EXTENSION }}
|
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_INVOICING_EXTENSION }}
|
||||||
|
|
||||||
|
|||||||
22
.github/workflows/build-private.yml
vendored
22
.github/workflows/build-private.yml
vendored
@@ -8,6 +8,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-private.yml'
|
- '.github/workflows/build-private.yml'
|
||||||
|
- 'extensions/manifest.json'
|
||||||
- 'docker/prod/**'
|
- 'docker/prod/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
permissions:
|
permissions:
|
||||||
@@ -72,10 +73,23 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
node-version: '20.x'
|
||||||
|
|
||||||
|
- name: "Read extension manifest"
|
||||||
|
id: extension-manifest
|
||||||
|
run: |
|
||||||
|
{
|
||||||
|
echo "billing_repository=$(jq -r '.Billing.repository' extensions/manifest.json)"
|
||||||
|
echo "billing_ref=$(jq -r '.Billing.ref' extensions/manifest.json)"
|
||||||
|
echo "services_repository=$(jq -r '.Services.repository' extensions/manifest.json)"
|
||||||
|
echo "services_ref=$(jq -r '.Services.ref' extensions/manifest.json)"
|
||||||
|
echo "invoicing_repository=$(jq -r '.Invoicing.repository' extensions/manifest.json)"
|
||||||
|
echo "invoicing_ref=$(jq -r '.Invoicing.ref' extensions/manifest.json)"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: "Checkout billing extension"
|
- name: "Checkout billing extension"
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: solidtime-io/extension-billing
|
repository: ${{ steps.extension-manifest.outputs.billing_repository }}
|
||||||
|
ref: ${{ steps.extension-manifest.outputs.billing_ref }}
|
||||||
path: extensions/Billing
|
path: extensions/Billing
|
||||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_BILLING_EXTENSION }}
|
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_BILLING_EXTENSION }}
|
||||||
|
|
||||||
@@ -95,7 +109,8 @@ jobs:
|
|||||||
- name: "Checkout services extension"
|
- name: "Checkout services extension"
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: solidtime-io/extension-services
|
repository: ${{ steps.extension-manifest.outputs.services_repository }}
|
||||||
|
ref: ${{ steps.extension-manifest.outputs.services_ref }}
|
||||||
path: extensions/Services
|
path: extensions/Services
|
||||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_SERVICES_EXTENSION }}
|
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_SERVICES_EXTENSION }}
|
||||||
|
|
||||||
@@ -113,7 +128,8 @@ jobs:
|
|||||||
- name: "Checkout invoicing extension"
|
- name: "Checkout invoicing extension"
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: solidtime-io/extension-invoicing
|
repository: ${{ steps.extension-manifest.outputs.invoicing_repository }}
|
||||||
|
ref: ${{ steps.extension-manifest.outputs.invoicing_ref }}
|
||||||
path: extensions/Invoicing
|
path: extensions/Invoicing
|
||||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_INVOICING_EXTENSION }}
|
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_INVOICING_EXTENSION }}
|
||||||
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,9 +26,10 @@ yarn-error.log
|
|||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
/coverage
|
/coverage
|
||||||
/extensions
|
/extensions/*
|
||||||
!/extensions/.gitkeep
|
!/extensions/.gitkeep
|
||||||
!/extensions/extensions_autoload.php
|
!/extensions/extensions_autoload.php
|
||||||
|
!/extensions/manifest.json
|
||||||
/auth.json
|
/auth.json
|
||||||
/modules_statuses.json
|
/modules_statuses.json
|
||||||
/k8s
|
/k8s
|
||||||
|
|||||||
@@ -116,12 +116,10 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
throw new ImportException('Time entry description is too long');
|
throw new ImportException('Time entry description is too long');
|
||||||
}
|
}
|
||||||
$timeEntry->description = $record['Description'];
|
$timeEntry->description = $record['Description'];
|
||||||
if (isset($record['Billable'])) {
|
if (! in_array($record['Billable'], ['Yes', 'No'], true)) {
|
||||||
if (! in_array($record['Billable'], ['Yes', 'No'], true)) {
|
throw new ImportException('Invalid billable value');
|
||||||
throw new ImportException('Invalid billable value');
|
|
||||||
}
|
|
||||||
$timeEntry->billable = $record['Billable'] === 'Yes';
|
|
||||||
}
|
}
|
||||||
|
$timeEntry->billable = $record['Billable'] === 'Yes';
|
||||||
$timeEntry->tags = $this->getTags($record['Tags']);
|
$timeEntry->tags = $this->getTags($record['Tags']);
|
||||||
$timeEntry->is_imported = true;
|
$timeEntry->is_imported = true;
|
||||||
|
|
||||||
@@ -221,6 +219,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
'Group',
|
'Group',
|
||||||
'Email',
|
'Email',
|
||||||
'Tags',
|
'Tags',
|
||||||
|
'Billable',
|
||||||
'Start Date',
|
'Start Date',
|
||||||
'Start Time',
|
'Start Time',
|
||||||
'End Date',
|
'End Date',
|
||||||
|
|||||||
14
extensions/manifest.json
Normal file
14
extensions/manifest.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Billing": {
|
||||||
|
"repository": "solidtime-io/extension-billing",
|
||||||
|
"ref": "v0.0.1"
|
||||||
|
},
|
||||||
|
"Services": {
|
||||||
|
"repository": "solidtime-io/extension-services",
|
||||||
|
"ref": "v0.0.1"
|
||||||
|
},
|
||||||
|
"Invoicing": {
|
||||||
|
"repository": "solidtime-io/extension-invoicing",
|
||||||
|
"ref": "v0.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -199,7 +199,7 @@ body {
|
|||||||
--muted-foreground: var(--color-text-tertiary);
|
--muted-foreground: var(--color-text-tertiary);
|
||||||
--accent: var(--color-bg-tertiary);
|
--accent: var(--color-bg-tertiary);
|
||||||
--accent-foreground: var(--color-text-primary);
|
--accent-foreground: var(--color-text-primary);
|
||||||
--destructive: 0 72% 60%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--destructive-foreground: var(--color-text-primary);
|
--destructive-foreground: var(--color-text-primary);
|
||||||
--border: var(--color-border-primary);
|
--border: var(--color-border-primary);
|
||||||
--input: var(--color-border-tertiary);
|
--input: var(--color-border-tertiary);
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
"Project","Client","Description","Task","User","Group","Email","Tags","Start Date","Start Time","End Date","End Time","Duration (h)","Duration (decimal)","Billable Rate (USD)","Billable Amount (USD)"
|
|
||||||
"Project without Client","","","","Peter Tester","","peter.test@email.test","Development, Backend","03/04/2024","10:23:52 AM","03/04/2024","10:23:52 AM","00:00:00","0.00","0.00","0.00"
|
|
||||||
"Project for Big Company","Big Company","Working hard","Task 1","Peter Tester","","peter.test@email.test","","03/04/2024","10:23 AM","03/04/2024","11:23:01 AM","01:00:01","0.00","0.00","0.00"
|
|
||||||
|
@@ -41,30 +41,6 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract
|
|||||||
$this->assertSame(1, $report->clientsCreated);
|
$this->assertSame(1, $report->clientsCreated);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_import_of_test_file_without_billable_works_and_defaults_to_non_billable(): void
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
$organization = Organization::factory()->create();
|
|
||||||
$timezone = 'Europe/Vienna';
|
|
||||||
$importer = new ClockifyTimeEntriesImporter;
|
|
||||||
$importer->init($organization);
|
|
||||||
$data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_4.csv');
|
|
||||||
|
|
||||||
// Act
|
|
||||||
$importer->importData($data, $timezone);
|
|
||||||
$report = $importer->getReport();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
$testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries(false, true);
|
|
||||||
$this->checkTimeEntries($testScenario, false, true);
|
|
||||||
$this->assertSame(2, $report->timeEntriesCreated);
|
|
||||||
$this->assertSame(2, $report->tagsCreated);
|
|
||||||
$this->assertSame(1, $report->tasksCreated);
|
|
||||||
$this->assertSame(1, $report->usersCreated);
|
|
||||||
$this->assertSame(2, $report->projectsCreated);
|
|
||||||
$this->assertSame(1, $report->clientsCreated);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_import_of_test_with_special_characters_description_succeeds(): void
|
public function test_import_of_test_with_special_characters_description_succeeds(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ImporterTestAbstract extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @return object{user1: User, project1: Project, project2: Project, tag1: Tag, tag2: Tag}
|
* @return object{user1: User, project1: Project, project2: Project, tag1: Tag, tag2: Tag}
|
||||||
*/
|
*/
|
||||||
protected function checkTestScenarioAfterImportExcludingTimeEntries(bool $detailed = false, bool $billableDefault = false): object
|
protected function checkTestScenarioAfterImportExcludingTimeEntries(bool $detailed = false): object
|
||||||
{
|
{
|
||||||
$users = User::all();
|
$users = User::all();
|
||||||
$this->assertCount(2, $users);
|
$this->assertCount(2, $users);
|
||||||
@@ -80,12 +80,12 @@ class ImporterTestAbstract extends TestCase
|
|||||||
$this->assertSame('#ef5350', $project1->color);
|
$this->assertSame('#ef5350', $project1->color);
|
||||||
$this->assertSame(null, $project1->billable_rate);
|
$this->assertSame(null, $project1->billable_rate);
|
||||||
// Project for Big Company
|
// Project for Big Company
|
||||||
$this->assertSame(! $billableDefault, $project2->is_billable);
|
$this->assertSame(true, $project2->is_billable);
|
||||||
$this->assertSame(false, $project2->is_public);
|
$this->assertSame(false, $project2->is_public);
|
||||||
$this->assertSame('#ec407a', $project2->color);
|
$this->assertSame('#ec407a', $project2->color);
|
||||||
$this->assertSame(10001, $project2->billable_rate);
|
$this->assertSame(10001, $project2->billable_rate);
|
||||||
// Project (Archived)
|
// Project (Archived)
|
||||||
$this->assertSame(! $billableDefault, $project3->is_billable);
|
$this->assertSame(true, $project3->is_billable);
|
||||||
$this->assertSame(true, $project3->is_public);
|
$this->assertSame(true, $project3->is_public);
|
||||||
$this->assertSame('#6a407f', $project3->color);
|
$this->assertSame('#6a407f', $project3->color);
|
||||||
$this->assertSame(null, $project3->billable_rate);
|
$this->assertSame(null, $project3->billable_rate);
|
||||||
@@ -176,7 +176,7 @@ class ImporterTestAbstract extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @param object{user1: User, project1: Project, project2: Project, tag1: Tag, tag2: Tag} $testScenario
|
* @param object{user1: User, project1: Project, project2: Project, tag1: Tag, tag2: Tag} $testScenario
|
||||||
*/
|
*/
|
||||||
protected function checkTimeEntries(object $testScenario, bool $secondRun = false, bool $billableDefault = false): void
|
protected function checkTimeEntries(object $testScenario, bool $secondRun = false): void
|
||||||
{
|
{
|
||||||
$timeEntries = TimeEntry::all();
|
$timeEntries = TimeEntry::all();
|
||||||
if ($secondRun) {
|
if ($secondRun) {
|
||||||
@@ -197,7 +197,7 @@ class ImporterTestAbstract extends TestCase
|
|||||||
$this->assertSame('Working hard', $timeEntry2->description);
|
$this->assertSame('Working hard', $timeEntry2->description);
|
||||||
$this->assertSame('2024-03-04 09:23:00', $timeEntry2->start->toDateTimeString());
|
$this->assertSame('2024-03-04 09:23:00', $timeEntry2->start->toDateTimeString());
|
||||||
$this->assertSame('2024-03-04 10:23:01', $timeEntry2->end->toDateTimeString());
|
$this->assertSame('2024-03-04 10:23:01', $timeEntry2->end->toDateTimeString());
|
||||||
$this->assertSame(! $billableDefault, $timeEntry2->billable);
|
$this->assertTrue($timeEntry2->billable);
|
||||||
$this->assertTrue($timeEntry2->is_imported);
|
$this->assertTrue($timeEntry2->is_imported);
|
||||||
$this->assertSame([], $timeEntry2->tags);
|
$this->assertSame([], $timeEntry2->tags);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user