Skip to content

Commit

Permalink
Merge branch 'feature/create-invoice-for-new-client' into 'master'
Browse files Browse the repository at this point in the history
Create invoice for new client

See merge request twistlabdev/mes-factures!6
  • Loading branch information
gfavre committed Aug 3, 2023
2 parents f6a6598 + c5625fd commit 7aee9ef
Show file tree
Hide file tree
Showing 20 changed files with 115 additions and 36 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.9.0] 2023-08-03
# Added
- Ability to create client from invoice


## [1.8.1] 2023-08-03
# Changed
- Do not display country if both client and company are Swiss.
- Access to clients and invoices pages is impossible if user has not defined a company yet.
- Add default 7.7% VAT Rate for new companies

# Fixed
- Could not save edited invoice
Expand Down
2 changes: 1 addition & 1 deletion beyondtheadmin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.8.1"
__version__ = "1.9.0"
__version_info__ = tuple(
[int(num) if num.isdigit() else num for num in __version__.replace("-", ".", 1).split(".")]
)
12 changes: 10 additions & 2 deletions beyondtheadmin/clients/clients_app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ export default {
clientUpdateUrl: '',
clientRedirectUrl: '',
companiesUrl: '',
}
},
redirectToInvoice: false,
}
},
methods: {
Expand All @@ -158,7 +159,11 @@ export default {
*/
this.savedComponentCount ++;
if (this.savedComponentCount === this.expectedComponentCount) {
window.location = this.urls.clientRedirectUrl;
let newLocation = this.urls.clientRedirectUrl;
if (this.redirectToInvoice) {
newLocation += `?client=${this.clientId}`
}
window.location = newLocation
}
},
focusFirstError(){
Expand Down Expand Up @@ -227,6 +232,9 @@ export default {
this.urls.clientRedirectUrl = this.$el.parentNode.dataset.clientRedirectUrl;
this.urls.companiesUrl = this.$el.parentNode.dataset.companiesUrl;
this.clientId = this.$el.parentNode.dataset.clientId;
if (this.$el.parentNode.dataset.redirectToInvoice === '1') {
this.redirectToInvoice = true;
}
this.$http.get(this.urls.companiesUrl).then(response => {
this.companies = response.data.results;
Expand Down
2 changes: 1 addition & 1 deletion beyondtheadmin/clients/static/clients_app/js/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion beyondtheadmin/clients/static/clients_app/js/app.js.map

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion beyondtheadmin/clients/templates/clients_app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
data-client-id="{{ client.id|default_if_none:"" }}"
data-client-create-url="{% url 'api:client-list' %}"
data-client-update-url="{{ client.api_url|default_if_none:"" }}"
data-client-redirect-url="{% url 'clients:list' %}"
data-client-redirect-url="{{ success_url }}"
data-redirect-to-invoice="{{ redirect_to_invoice }}"
data-companies-url="{% url 'api:company-list' %}"
></div>
</article>
Expand Down
28 changes: 28 additions & 0 deletions beyondtheadmin/clients/views/dashboard.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import uuid

from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.views.generic import DeleteView, ListView, TemplateView

from beyondtheadmin.dashboard.permissions import HasCompanyMixin
from beyondtheadmin.invoices.models import Invoice

from ..models import Client


def is_valid_uuid(val):
try:
uuid.UUID(str(val))
return True
except ValueError:
return False


class ClientAppView(LoginRequiredMixin, HasCompanyMixin, TemplateView):
template_name = "clients_app/index.html"

Expand All @@ -18,6 +29,23 @@ def get_context_data(self, **kwargs):
context["client"] = get_object_or_404(
Client, pk=self.kwargs["pk"], company__users=self.request.user
)
invoice_param = self.request.GET.get("invoice", None)
if invoice_param:
context["redirect_to_invoice"] = 1
if is_valid_uuid(invoice_param):
try:
invoice = Invoice.objects.get(pk=invoice_param)
if not invoice.check_rights(self.request.user):
raise Invoice.DoesNotExist()
success_url = invoice.get_edit_url()
except Invoice.DoesNotExist:
success_url = reverse_lazy("invoices:create")
else:
success_url = reverse_lazy("invoices:create")
else:
context["redirect_to_invoice"] = 0
success_url = reverse_lazy("clients:list")
context["success_url"] = success_url
return context


Expand Down
2 changes: 1 addition & 1 deletion beyondtheadmin/companies/companies_app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export default {
vatId: '',
vatEnabled: true,
vatRate: 0,
vatRate: 0.077,
hourlyRate: 0.0,
paymentDelay: 30,
Expand Down
2 changes: 1 addition & 1 deletion beyondtheadmin/companies/static/companies_app/js/app.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

27 changes: 22 additions & 5 deletions beyondtheadmin/invoices/invoices_app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,18 @@
<label for="id_client" class=" requiredField">
{{ $t("Client") }}<span class="asteriskField">*</span>
</label>
<typeahead-input :items="clients" @select="handleClientSelect"
:value="selectedClientName"
:class="{ 'is-invalid': v$.client.$error }"
></typeahead-input>
<div class="input-group">
<typeahead-input :items="clients" @select="handleClientSelect"
:value="selectedClientName"
:class="{ 'is-invalid': v$.client.$error }"
:is-grouped="true"
></typeahead-input>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" @click.prevent="createClient">
{{ $t("New client")}}
</button>
</div>
</div>
<span class="invalid-feedback" v-if="v$.client.required.$invalid">
{{ $t("This field is required") }}
</span>
Expand Down Expand Up @@ -326,6 +334,7 @@ export default {
vatRatePercent: 7.7,
urls: {
companiesUrl: "",
createClientUrl: "",
clientsUrl: "",
invoiceUrl: "",
invoicesUrl: "",
Expand Down Expand Up @@ -453,7 +462,14 @@ export default {
console.error(error)
});
},
async createClient() {
if (this.invoice.id) {
await this.saveInvoice(false);
window.location.href = this.urls.createClientUrl + `?invoice=${this.invoice.id}`;
} else {
window.location.href = this.urls.createClientUrl + `?invoice=new`;
}
},
async saveInvoice(redirect) {
const validationResult = await this.v$.$validate();
Expand Down Expand Up @@ -607,6 +623,7 @@ export default {
mounted() {
this.$i18n.locale = this.$el.parentNode.dataset.languageCode;
this.urls.createClientUrl = this.$el.parentNode.dataset.createClientUrl;
this.urls.companiesUrl = this.$el.parentNode.dataset.companiesUrl;
this.urls.clientsUrl = this.$el.parentNode.dataset.clientsUrl;
this.urls.invoicesUrl = this.$el.parentNode.dataset.invoicesUrl;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<template>
<div class="typeahead-wrap" :class="$attrs.class">
<div class="input-wrap" >
<input type="text" class="form-control typeahead"
v-model="searchQuery"
@input="searchItems" @focus="searchItems" />
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search tp-icon search-icon" viewBox="0 0 16 16"><path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="currentColor" aria-hidden="true" class="tp-icon clear-icon" :class="$attrs.class" @click="clearSearchQuery"><path d="M23.057 7.057l-16 16c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l16-16c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0z"></path><path d="M7.057 8.943l16 16c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885l-16-16c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"></path></svg>
</div>
<div class="dropdown">
<ul class="dropdown-menu show" :class="{ 'd-none': !isDropdownOpen }" ref="dropdownMenu">
<li v-for="(item, index) in highlightedItems" :key="item.id" class="dropdown-item"
@click="selectItem(item)" :class="{ 'bg-light': index === highlightedIndex }">
<span v-html="item.highlightedName"></span>
</li>
</ul>
</div>
<div class="typeahead-wrap" :class="containerClass" >
<div class="input-wrap" >
<input type="text" class="form-control typeahead"
v-model="searchQuery"
@input="searchItems" @focus="searchItems"
/>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search tp-icon search-icon" viewBox="0 0 16 16"><path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="currentColor" aria-hidden="true" class="tp-icon clear-icon" :class="$attrs.class" @click="clearSearchQuery"><path d="M23.057 7.057l-16 16c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l16-16c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0z"></path><path d="M7.057 8.943l16 16c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885l-16-16c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"></path></svg>
</div>
<div class="dropdown">
<ul class="dropdown-menu show" :class="{ 'd-none': !isDropdownOpen }" ref="dropdownMenu">
<li v-for="(item, index) in highlightedItems" :key="item.id" class="dropdown-item"
@click="selectItem(item)" :class="{ 'bg-light': index === highlightedIndex }">
<span v-html="item.highlightedName"></span>
</li>
</ul>
</div>
</div>
</template>

<script>
Expand All @@ -29,6 +30,10 @@ export default {
type: String,
default: '',
},
isGrouped: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand All @@ -52,6 +57,10 @@ export default {
be modified without affecting the original array. */
return [...this.items];
},
containerClass(){
const additionalClass = this.isGrouped ? 'grouped' : '';
return `${this.$attrs.class} ${additionalClass}`;
},
},
methods: {
closeDropdown() {
Expand Down Expand Up @@ -133,6 +142,13 @@ export default {
};
</script>
<style>
.grouped {
flex: 1 1 auto;
}
.grouped .form-control{
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.bi {
display: inline-block;
font-size: 1rem;
Expand Down
3 changes: 2 additions & 1 deletion beyondtheadmin/invoices/invoices_app/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
"This field is required as start of invoice period has been set": "Dieses Feld ist erforderlich, da der Beginn des Rechnungszeitraums festgelegt wurde.",
"At least one line is required": "Mindestens eine Zeile ist erforderlich",
"Should be > 0": "Sollte > 0 sein",
"Has to be later than start of invoice period": "Muss später als der Beginn des Rechnungszeitraums sein"
"Has to be later than start of invoice period": "Muss später als der Beginn des Rechnungszeitraums sein",
"New client": "Neuer Kunde"
}
3 changes: 2 additions & 1 deletion beyondtheadmin/invoices/invoices_app/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
"This field is required as start of invoice period has been set": "This field is required as start of invoice period has been set",
"At least one line is required": "At least one line is required",
"Should be > 0": "Should be > 0",
"Has to be later than start of invoice period": "Has to be later than start of invoice period"
"Has to be later than start of invoice period": "Has to be later than start of invoice period",
"New client": "New client"
}
3 changes: 2 additions & 1 deletion beyondtheadmin/invoices/invoices_app/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
"This field is required as start of invoice period has been set": "Ce champ est obligatoire puisque le début de la période de facturation a été défini",
"At least one line is required": "Au moins une ligne est requise",
"Should be > 0": "Doit être > 0",
"Has to be later than start of invoice period": "Doit être postérieur au début de la période de facturation"
"Has to be later than start of invoice period": "Doit être postérieur au début de la période de facturation",
"New client": "Nouveau client"
}
2 changes: 1 addition & 1 deletion beyondtheadmin/invoices/static/invoices_app/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion beyondtheadmin/invoices/static/invoices_app/js/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion beyondtheadmin/invoices/static/invoices_app/js/app.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions beyondtheadmin/invoices/templates/invoices_app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<div id="app"
data-language-code="{{ LANGUAGE_CODE }}"
data-create-client-url="{% url 'clients:create' %}"
data-clients-url="{% url 'api:client-list' %}"
data-companies-url="{% url 'api:company-list' %}"
data-invoices-url="{% url 'api:invoice-list' %}"
Expand Down
Binary file modified beyondtheadmin/static/images/anne-nygard-OtqaCE_SEMI-unsplash.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7aee9ef

Please sign in to comment.