Skip to content

Commit

Permalink
feat: fixed rate
Browse files Browse the repository at this point in the history
  • Loading branch information
mp3000mp committed Sep 18, 2024
1 parent 6ede04f commit d5e06cd
Show file tree
Hide file tree
Showing 27 changed files with 782 additions and 487 deletions.
33 changes: 33 additions & 0 deletions backend/migrations/Version20240918230104.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240918230104 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE tender_row ADD fixed_rate DOUBLE PRECISION');
$this->addSql('UPDATE tender_row SET fixed_rate = 0');
$this->addSql('ALTER TABLE tender_row CHANGE fixed_rate fixed_rate DOUBLE PRECISION NOT NULL');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE tender_row DROP fixed_rate');
}
}
11 changes: 11 additions & 0 deletions backend/src/Entity/Tender.php
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,17 @@ public function getSoldDays(): float
return round($soldDays, 2);
}

#[Groups(['tender_show', 'tender_list', 'opportunity_list', 'opportunity_show', 'company_show', 'contact_show'])]
public function getTotalRate(): float
{
$total = 0;
foreach ($this->tenderRows as $tenderRow) {
$total += $tenderRow->getFixedRate() + $tenderRow->getSoldDays()*$this->getAverageDailyRate();
}

return $total;
}

public function getAmount(): float
{
return $this->getSoldDays() * $this->averageDailyRate;
Expand Down
23 changes: 23 additions & 0 deletions backend/src/Entity/TenderRow.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class TenderRow
#[Assert\GreaterThan(0)]
private int $position;

#[ORM\Column]
#[Groups(['tender_show', 'tender_row_add', 'tender_row_edit'])]
#[Assert\GreaterThanOrEqual(0)]
private float $fixedRate = 0;

#[ORM\Column]
#[Groups(['tender_show', 'tender_row_add', 'tender_row_edit'])]
#[Assert\GreaterThanOrEqual(0)]
Expand Down Expand Up @@ -69,6 +74,18 @@ public function setPosition(int $position): self
return $this;
}

public function getFixedRate(): float
{
return $this->fixedRate;
}

public function setFixedRate(float $fixedRate): self
{
$this->fixedRate = $fixedRate;

return $this;
}

public function getSoldDays(): float
{
return $this->soldDays;
Expand All @@ -81,6 +98,12 @@ public function setSoldDays(float $soldDays): self
return $this;
}

#[Groups(['tender_show', 'tender_row_add', 'tender_row_edit'])]
public function getTotalRate(): float
{
return $this->getFixedRate() + $this->getTender()->getAverageDailyRate()*$this->getSoldDays();
}

public function getTitle(): string
{
return $this->title;
Expand Down
1 change: 0 additions & 1 deletion frontend/src/assets/style/bootstrap/main.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

@import '~bootstrap/scss/functions';

@import 'vars-override';
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Mp3000Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ withDefaults(
<slot name="filters"></slot>
</div>
<div class="table-responsive">
<table class="table table-hover">
<table class="table table-hover table-striped table-sm">
<thead>
<slot name="header"></slot>
</thead>
Expand Down
37 changes: 15 additions & 22 deletions frontend/src/components/Mp3000TableHeader.vue
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
<script lang="ts" setup>
import type { Sorter } from '@/misc/sorter'
const props = defineProps<{
label: string
property: string
sorter: Sorter<any>
}>()
function sort() {
if (props.sorter.isAsc(props.property) === false) {
props.sorter.removeSort(props.property)
} else {
props.sorter.addSort(props.property)
withDefaults(
defineProps<{
label: string
asc?: boolean | null
priority?: number
}>(),
{
asc: null,
priority: 0
}
}
)
</script>

<template>
<th @click.prevent="sort()" class="sort-header cp">
<th class="sort-header cp">
<span>{{ label }}</span>
<template v-if="sorter.getPriority(property) > 0">
<template v-if="priority > 0">
<span>
<font-awesome-icon
class="ms-1"
:icon="['fa', sorter.isAsc(property) ? 'sort-down' : 'sort-up']"
/>
<font-awesome-icon class="ms-1" :icon="['fa', asc ? 'sort-down' : 'sort-up']" />
</span>
<span class="sort-priority" :class="[sorter.isAsc(property) ? 'sort-down' : 'sort-up']">
{{ sorter.getPriority(property) }}
<span class="sort-priority" :class="[asc ? 'sort-down' : 'sort-up']">
{{ priority }}
</span>
</template>
</th>
Expand Down
157 changes: 157 additions & 0 deletions frontend/src/composables/__test__/useSorter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { describe, expect, test } from 'vitest'
import dayjs, { Dayjs } from 'dayjs'
import { Ref, ref } from 'vue'
import { useSorter, SortConfigTypeEnum } from '@/composables/useSorter'

interface ListElement {
id: number
propString: string
propNumber: number
propDate: Dayjs
customProp: string
}

function getList(): Ref<ListElement[]> {
return ref([
{ id: 1, propString: 'a', propNumber: 3, propDate: dayjs('2023-11-01'), customProp: '10' },
{ id: 2, propString: 'b', propNumber: 2, propDate: dayjs('2023-11-03'), customProp: '2' },
{ id: 3, propString: 'c', propNumber: 1, propDate: dayjs('2023-11-02'), customProp: '3' }
])
}

describe('useSorter.ts', () => {
test('sorts number prop', () => {
const { sort, sortedList } = useSorter(
[{ property: 'propNumber', type: SortConfigTypeEnum.NUMBER }],
getList()
)

sort('propNumber')
expect(sortedList.value.map((s) => s.id)).toEqual([3, 2, 1])
})

test('sorts string prop', () => {
const { sort, sortedList } = useSorter(
[{ property: 'propString', type: SortConfigTypeEnum.STRING }],
getList()
)

sort('propString')
expect(sortedList.value.map((s) => s.id)).toEqual([1, 2, 3])
})

test('sorts date prop', () => {
const { sort, sortedList } = useSorter(
[{ property: 'propDate', type: SortConfigTypeEnum.DATE }],
getList()
)

sort('propDate')
expect(sortedList.value.map((s) => s.id)).toEqual([1, 3, 2])
})

test('sorts with custom function', () => {
const { sort, sortedList } = useSorter(
[
{
property: 'customProp',
type: SortConfigTypeEnum.CUSTOM,
customCompare: (a, b) => Number(a.customProp) - Number(b.customProp)
}
],
getList()
)

sort('customProp')
expect(sortedList.value.map((s) => s.id)).toEqual([2, 3, 1])
})

test('sorts null values', () => {
const list = getList()
const { sort, sortedList } = useSorter(
[{ property: 'propNumber', type: SortConfigTypeEnum.NUMBER }],
list
)

list.value[0].propNumber = null
sort('propNumber')
expect(sortedList.value.map((s) => s.id)).toEqual([1, 3, 2])
})

test('sorts with several props', () => {
const list = getList()
const { sort, sortedList } = useSorter(
[
{ property: 'propNumber', type: SortConfigTypeEnum.NUMBER },
{ property: 'propDate', type: SortConfigTypeEnum.DATE }
],
list
)

list.value[1].propNumber = 1
sort('propNumber')
sort('propDate')
expect(sortedList.value.map((s) => s.id)).toEqual([3, 2, 1])
})

test('handles props priority', () => {
const { getPriority, sort } = useSorter(
[
{ property: 'propNumber', type: SortConfigTypeEnum.NUMBER },
{ property: 'propString', type: SortConfigTypeEnum.STRING },
{ property: 'propDate', type: SortConfigTypeEnum.DATE }
],
getList()
)

expect(getPriority('propNumber')).toBe(0)
sort('propNumber')
expect(getPriority('propNumber')).toBe(1)
sort('propNumber')
expect(getPriority('propNumber')).toBe(1)
sort('propString')
expect(getPriority('propString')).toBe(2)
sort('propDate')
expect(getPriority('propDate')).toBe(3)
sort('propString')
expect(getPriority('propNumber')).toBe(1)
expect(getPriority('propDate')).toBe(2)
expect(getPriority('propString')).toBe(3)
sort('propString')
expect(getPriority('propNumber')).toBe(1)
expect(getPriority('propString')).toBe(0)
expect(getPriority('propDate')).toBe(2)
sort('propString')
expect(getPriority('propString')).toBe(3)
})

test('handles props asc', () => {
const { getAsc, sort } = useSorter(
[{ property: 'propNumber', type: SortConfigTypeEnum.NUMBER }],
getList()
)

expect(getAsc('propNumber')).toBe(null)
sort('propNumber', false)
expect(getAsc('propNumber')).toBeFalsy()
sort('propNumber')
expect(getAsc('propNumber')).toBe(null)
sort('propNumber')
expect(getAsc('propNumber')).toBeTruthy()
sort('propNumber')
expect(getAsc('propNumber')).toBeFalsy()
sort('propNumber', true)
expect(getAsc('propNumber')).toBeTruthy()

Check failure on line 144 in frontend/src/composables/__test__/useSorter.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-tests

src/composables/__test__/useSorter.test.ts > useSorter.ts > handles props asc

AssertionError: expected null to be truthy - Expected: null + Received: false ❯ src/composables/__test__/useSorter.test.ts:144:34
})

test('resets sorted props', () => {
const { getPriority, sort, resetSorts } = useSorter(
[{ property: 'propNumber', type: SortConfigTypeEnum.NUMBER }],
getList()
)

sort('propNumber')
resetSorts()
expect(getPriority('propNumber')).toBe(0)
})
})
Loading

0 comments on commit d5e06cd

Please sign in to comment.