Browse Source

Merge branch 'invoice' of Simondb/Eindwerk-Projectopvolging-Facturatie into master

improvements
Simondb 3 years ago
parent
commit
78e14e8ba8

+ 6
- 3
backend/lib/Controller/ProjectsController.php View File

@@ -56,11 +56,13 @@ class ProjectsController
public function saveTask(Request $request, Response $response, array $args)
{
$db = $this->container->get('db');
$query = $db->prepare('UPDATE task SET name = :name, description = :description WHERE id = :id');
$query = $db->prepare('UPDATE task SET name = :name, description = :description, assignee_id = :userId, time = :time WHERE id = :id');

$query->bindValue(':name', $_POST['name']);
$query->bindValue(':description', $_POST['description']);
$query->bindValue(':id', $_POST['id']);
$query->bindValue(':userId', $_POST['userId']);
$query->bindValue(':time', $_POST['time']);

if ($query->execute()) {
$response->getBody()->write(strval($db->lastInsertRowID()));
@@ -103,9 +105,10 @@ class ProjectsController

$db = $this->container->get('db');
$query = $db->prepare('
SELECT T.name, T.description, T.state_id, T.id, T.row
SELECT T.name, T.description, T.state_id, T.assignee_id, E.user_name, T.id, T.row, T.time
FROM task T
JOIN project P on T.project_id = P.id
JOIN employee E on T.assignee_id = E.id
WHERE T.project_id = :id
ORDER BY row
');
@@ -137,7 +140,7 @@ class ProjectsController
{
$db = $this->container->get('db');
$query = $db->prepare('
SELECT P.id, P.name, P.description, P.team_id, T.name as team_name, P.client_id, C.name as client_name
SELECT P.id, P.name, P.description, P.team_id, P.city, P.country, T.name as team_name, P.client_id, C.name as client_name, C.btw_nr, C.address
FROM project P
LEFT JOIN team T
ON P.team_id = T.id

+ 2
- 1
backend/lib/Entity/Task.php View File

@@ -17,9 +17,10 @@ class Task
{
$db = $this->container->get('db');
$query = $db->prepare('
SELECT T.name, T.description, T.state_id, T.id, T.row
SELECT T.name, T.description, T.state_id, E.user_name, T.id, T.row, T.time
FROM task T
JOIN project P on T.project_id = p.id
JOIN employee E on T.assignee_id = E.id
WHERE T.project_id = 1
ORDER BY row
');

+ 0
- 1
frontend/layouts/nav&foot.vue View File

@@ -8,7 +8,6 @@
<li class="test"><nuxt-link to="/kanban">Kanban</nuxt-link></li>
<li class="test"><nuxt-link to="/projects">Projects</nuxt-link></li>
<li class="test"><nuxt-link to="/teams">Teams</nuxt-link></li>
<li class="test"><nuxt-link to="/factuur">Facturatie</nuxt-link></li>
<li class="test">
<nuxt-link to="/clients">Bedrijfsgegevens</nuxt-link>
</li>

+ 0
- 92
frontend/pages/factuur.vue View File

@@ -1,92 +0,0 @@
<template>
<div class="container">
<div class="flex-container">
<div>
<input v-model="search" type="text" placeholder="projects" />
</div>
<div>
<table>
<thead>
<tr>
<th>
name
</th>
<th>
description
</th>
</tr>
</thead>
<tbody v-if="!loading && !search">
<tr v-for="project in projects" :key="project.id">
<td>{{ project.name }}</td>
<td>{{ project.description }}</td>
<button>Maak factuur</button>
</tr>
</tbody>
<tbody v-if="!loading && search">
<tr v-for="project in filteredProjects" :key="project.id">
<td>{{ project.name }}</td>
<td>{{ project.description }}</td>
<button>Maak factuur</button>
</tr>
</tbody>
<p v-if="loading">loading...</p>
</table>
</div>
</div>
</div>
</template>

<script>
import { mapState, mapActions } from 'vuex';
export default {
data() {
return {
filterTimeout: null,
search: '',
filteredProjects: [],
};
},
layout: 'nav&foot',
computed: {
...mapState({ projects: (state) => state.projects.list }),
},
watch: {
search(val) {
this.filteredProjects = this.projects.filter(
(project) =>
project.description.toLowerCase().includes(val.toLowerCase()) ||
project.name.toLowerCase().includes(val.toLowerCase()),
);
},
},
// ↓ put it in for reference ↓
/* watch: {
$route: {
immediate: true,
handler(route) {
this.search = route.query.name;
console.log('test');
this.pullChar({ pageNumber: this.pageNumber, name: this.search });
// this.pullChar should be this.pullProjects
},
},
}, */
mounted() {
if (!this.hasProjects) this.fetchAllProjects();
},
methods: {
...mapActions({ fetchAllProjects: 'projects/fetchAllProjects' }),
},
// ↓ put it in for reference ↓
/* toSearch() {
clearTimeout(this.filterTimeout);
this.filterTimeout = setTimeout(() => {
this.$router.push({
path: '/facturatie/1',
query: { name: document.getElementById('filterInp').value },
});
}, 1000);
}, */
};
</script>

+ 73
- 44
frontend/pages/kanban.vue View File

@@ -58,34 +58,48 @@
</div>
<div class="info-container">
<div>test</div>
<input id="text" type="text" placeholder="users" />
<input
id="assign"
v-model="user.user_name"
type="text"
placeholder="assign user"
autocomplete="off"
@focus="modal = true"
/>
<div v-if="filteredUser && modal">
<ul class="filter">
<li
v-for="filteredUser in filteredUsers"
:key="filteredUser.id"
@click="setUser(filteredUser)"
>
{{ filteredUser.user_name }}
</li>
</ul>
</div>
<textarea
id="textarea"
name=""
cols="30"
rows="10"
placeholder="data"
></textarea>
<button class="button" @click="saveTask">Save</button>
<FormulateForm @submit="saveTask">
<FormulateInput
id="text"
v-model="selectedTask.name"
type="text"
placeholder="Title"
/>
<FormulateInput
id="assign"
v-model="selectedTask.user_name"
type="text"
placeholder="assign user"
autocomplete="off"
@focus="modal = true"
/>
<div>
<ul class="filter">
<li
v-for="editTeam in editTeams"
:key="editTeam.id"
@click="setUser(editTeam)"
>
{{ editTeam.user_name }} <br />
</li>
</ul>
</div>
<FormulateInput
id="textarea"
v-model="selectedTask.description"
type="textarea"
name=""
cols="30"
rows="10"
placeholder="data"
/>
<FormulateInput
v-model="selectedTask.time"
type="number"
placeholder="Worked hrs."
/>
<FormulateInput type="submit" value="save" label="Save" />
</FormulateForm>
<button class="button" @click="deleteTask">Delete</button>
</div>
</div>
@@ -126,7 +140,7 @@ export default {
user: { id: 1, user_name: '' },
users: [],
filteredUsers: [],
editTeam: [],
editTeams: [],
};
},
computed: {
@@ -175,7 +189,8 @@ export default {
},
setUser(user) {
this.user = JSON.parse(JSON.stringify(user));
this.editTeam.push(user);
console.log(user);
this.selectedTask.user_name = user.user_name;
this.modal = false;
},
newTask() {
@@ -243,22 +258,23 @@ export default {
// console.log(arguments);
},
getTaskData(taskItem, e, task) {
const text = document.getElementById('text');
const textArea = document.getElementById('textarea');
// get data from card
this.selectedTask = taskItem;
this.selectedTask = JSON.parse(JSON.stringify(taskItem));
console.log(this.selectedTask);
// set data from card
text.value = this.selectedTask.name;
textArea.value = this.selectedTask.description;
},
saveTask() {
const text = document.getElementById('text');
const textArea = document.getElementById('textarea');
let employeeId;
this.teamMembers.forEach((element) => {
if (element.user_name === this.selectedTask.user_name) {
employeeId = element.employee_id;
}
});
const taskData = new URLSearchParams();
taskData.append('name', text.value);
taskData.append('description', textArea.value);
taskData.append('name', this.selectedTask.name);
taskData.append('description', this.selectedTask.description);
taskData.append('id', this.selectedTask.id);
taskData.append('userId', employeeId);
taskData.append('time', this.selectedTask.time);
this.$axios
.post('/saveTask', taskData)
.then(() => {
@@ -276,13 +292,14 @@ export default {
(obj) => obj.id === Number(this.projectId),
).name;
this.fetchMergeStates({ id: this.projectId });
this.teamMembers = [];
this.editTeam = [];
console.log(this.teamMembers);
this.editTeams = [];
this.teamMembers.forEach((element) => {
if (element.team_id === 5) {
this.editTeam.push(element);
if (element.team_id === Number(this.projectId)) {
this.editTeams.push(element);
}
});
console.log(this.editTeams);
},
deleteTask() {
const taskData = new URLSearchParams();
@@ -313,6 +330,14 @@ export default {
// });
// },
},
watch: {
user: {
deep: true,
handler() {
this.filteredU();
},
},
},
};
</script>

@@ -528,4 +553,8 @@ textarea {
font-size: 16px;
border-radius: 0.25rem;
}

.timeField {
height: 50px;
}
</style>

+ 5
- 0
frontend/pages/projects/index.vue View File

@@ -27,6 +27,11 @@
meer info
</n-link>
</td>
<td>
<n-link :to="'/projects/invoice/' + project.id">
make invoice
</n-link>
</td>
</tr>
</tbody>
<tbody v-if="!loading && search">

+ 227
- 0
frontend/pages/projects/invoice/_id.vue View File

@@ -0,0 +1,227 @@
<template>
<div class="invoice-container">
<div class="invoice-header">
<div class="invoice-header-items">
<h2>Invoice</h2>
<div>
<div>
<h2>{{ project.name }}</h2>
</div>
<div>{{ project.team_name }}</div>
<div>{{ project.city }}</div>
<div>{{ project.country }}</div>
</div>
</div>
</div>
<div class="invoice">
<div class="invoice-top">
<div>
<h5>BILL TO:</h5>
<div>{{ project.client_name }}</div>
<div>{{ project.btw_nr }}</div>
<div>{{ project.address }}</div>
</div>
<div>
<h5>invoice #</h5>
<div>{{ InvoiceNumber }}</div>
<h5>Date</h5>
<div>{{ Today }}</div>
<h5>Invoie due date</h5>
<div>{{ InvoiceDueDate }}</div>
</div>
</div>
<div class="invoice-bottom">
<table class="invoice-table">
<thead>
<tr>
<th>
TASKS
</th>
<th>
DESCRPTION
</th>
<th>
HRS
</th>
<th>
PRICE
</th>
<th>
TAX
</th>
<th>
AMOUNT
</th>
</tr>
</thead>
<tbody v-for="state in mergeStates" :key="state.id">
<tr v-for="Taskitem in state.tasks" :key="Taskitem.id">
<td>{{ Taskitem.name }}</td>
<td>{{ Taskitem.description }}</td>
<td>{{ Taskitem.time }}</td>
<td>50</td>
<td>20%</td>
<td>
{{ ((50 * Taskitem.time) / 100) * 20 + 50 * Taskitem.time }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="invoice-footer">
<div class="invoice-footer-left">
<h5>Extra:</h5>
<div class="invoice-footer-description">{{ project.description }}</div>
</div>
<div class="invoice-footer-right">
<div>Total</div>
<div>€{{ Total }}</div>
</div>
</div>
<Loader />
</div>
</template>

<script>
import { mapState, mapActions } from 'vuex';
import Loader from '~/components/Loader';
export default {
components: { Loader },
data() {
return {
id: Number(this.$route.params.id),
AllTasks: [],
GetAllTasks: [],
Total: 0,
InvoiceNumber: '',
Today: '',
InvoiceDueDate: '',
};
},
layout: 'nav&foot',
computed: {
...mapState({
project: (state) => state.projects.currentProject,
loading: 'loading',
}),
...mapState({
projects: (state) => state.mergeStates,
mergeStates: 'mergeStates',
loading: 'loading',
}),
},
created() {},
mounted() {
this.id = Number(this.$route.params.id);
this.fetchMergeStates({ id: this.id });
console.log(this.id);
this.fetchProject(this.id);

this.CreateInvoiceNumber();
this.GetDate();
this.Calculate();
},
methods: {
...mapActions({
fetchProject: 'projects/fetchProject',
saveProject: 'projects/saveProject',
}),
...mapActions(['fetchMergeStates']),
Calculate() {
this.fetchMergeStates({ id: this.id });
this.Total = 0;
console.log(this.mergeStates);
this.mergeStates.forEach((state) => {
state.tasks.forEach((Taskitem) => {
this.Total += ((50 * Taskitem.time) / 100) * 20 + 50 * Taskitem.time;
});
});
console.log(this.Total);
},
CreateInvoiceNumber() {
this.InvoiceNumber = this.id.toString().padStart(5, '0');
},
GetDate() {
const today = new Date();
let date = today.toLocaleDateString();
this.Today = date;
today.setMonth(today.getMonth() + 2);
date = today.toLocaleDateString();
this.InvoiceDueDate = date;
},
},
};
</script>

<style>
.invoice-container {
flex-direction: column;
display: flex;
}
.invoice-header {
height: 200px;
width: 100%;
background-color: #2065a8;
display: flex;
}
.invoice-header-items {
align-self: center;
width: 100%;
justify-content: space-between;
padding: 50px;
display: flex;
color: white;
}
.invoice-footer {
display: flex;
height: 150px;
width: 100%;
flex-direction: row;
}
.invoice-footer-left {
background-color: #bcdff5;
width: 65%;
display: block;
padding-top: 50px;
padding-left: 20px;
}
.invoice-footer-left.invoice-footer-description {
width: 150px;
overflow: hidden;
height: 1em;
}
.invoice-footer-right {
background-color: #2065a8;
width: 35%;
font-size: 50px;
color: white;
padding: 15px;
float: left;
}
.invoice {
display: flex;
flex-direction: column;
height: auto;
}
.invoice-top {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 50px;
height: 35%;
border-bottom: 1px solid black;
width: 100%;
}
.invoice-bottom {
padding: 50px;
}
.invoice-table {
width: 100%;
}
.invoice-table td,
.invoice-table th {
padding: 15px;
text-align: left;
}
</style>

+ 2
- 0
frontend/store/index.js View File

@@ -69,6 +69,7 @@ export const actions = {
.catch((error) => console.error(error));
},
fetchMergeStates({ commit }, { id }) {
commit('setLoading', true, { root: true });
const params = {
project_id: id,
};
@@ -76,6 +77,7 @@ export const actions = {
.get('/mergeStates', { params })
.then((res) => {
commit('setMergeStates', res.data);
commit('setLoading', false, { root: true });
})
.catch((error) => console.error(error));
},

Loading…
Cancel
Save