Compare commits
4 Commits
2299ec9f8c
...
183b8d3ed9
Author | SHA1 | Date |
---|---|---|
jordi fita mas | 183b8d3ed9 | |
jordi fita mas | a068784a22 | |
jordi fita mas | f917ce84dd | |
jordi fita mas | eb845edf0a |
|
@ -11,6 +11,7 @@ Build-Depends:
|
||||||
golang-github-julienschmidt-httprouter-dev,
|
golang-github-julienschmidt-httprouter-dev,
|
||||||
golang-github-leonelquinteros-gotext-dev,
|
golang-github-leonelquinteros-gotext-dev,
|
||||||
golang-golang-x-text-dev,
|
golang-golang-x-text-dev,
|
||||||
|
golang-github-tealeg-xlsx-dev,
|
||||||
postgresql-all (>= 217~),
|
postgresql-all (>= 217~),
|
||||||
sqitch,
|
sqitch,
|
||||||
pgtap,
|
pgtap,
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
-- Deploy numerus:import_contact to pg
|
||||||
|
-- requires: schema_numerus
|
||||||
|
-- requires: roles
|
||||||
|
-- requires: contact
|
||||||
|
-- requires: contact_web
|
||||||
|
-- requires: contact_phone
|
||||||
|
-- requires: contact_email
|
||||||
|
-- requires: contact_iban
|
||||||
|
-- requires: contact_swift
|
||||||
|
-- requires: contact_tax_details
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus, public;
|
||||||
|
|
||||||
|
create or replace function begin_import_contacts() returns name as
|
||||||
|
$$
|
||||||
|
create temporary table imported_contact (
|
||||||
|
contact_id integer
|
||||||
|
, name text not null default ''
|
||||||
|
, vatin text not null default ''
|
||||||
|
, email text not null default ''
|
||||||
|
, phone text not null default ''
|
||||||
|
, web text not null default ''
|
||||||
|
, address text not null default ''
|
||||||
|
, city text not null default ''
|
||||||
|
, province text not null default ''
|
||||||
|
, postal_code text not null default ''
|
||||||
|
, country_code text not null default ''
|
||||||
|
, iban text not null default ''
|
||||||
|
, bic text not null default ''
|
||||||
|
, tags text not null default ''
|
||||||
|
);
|
||||||
|
select 'imported_contact'::name;
|
||||||
|
$$
|
||||||
|
language sql;
|
||||||
|
|
||||||
|
revoke execute on function begin_import_contacts() from public;
|
||||||
|
grant execute on function begin_import_contacts() to invoicer;
|
||||||
|
grant execute on function begin_import_contacts() to admin;
|
||||||
|
|
||||||
|
create or replace function end_import_contacts(company_id integer) returns integer as
|
||||||
|
$$
|
||||||
|
declare
|
||||||
|
imported integer;
|
||||||
|
begin
|
||||||
|
update imported_contact
|
||||||
|
set country_code = upper(trim(country_code))
|
||||||
|
, name = trim(name)
|
||||||
|
, vatin = trim(vatin)
|
||||||
|
, email = trim(email)
|
||||||
|
, phone = trim(phone)
|
||||||
|
, web = trim(web)
|
||||||
|
, address = trim(address)
|
||||||
|
, city = trim(city)
|
||||||
|
, province = trim(province)
|
||||||
|
, postal_code = trim(postal_code)
|
||||||
|
, iban = trim(iban)
|
||||||
|
, bic = trim(bic)
|
||||||
|
, tags = lower(trim(regexp_replace(regexp_replace(tags, '[^\sA-Za-z0-9-]', '', 'g'), '\s\s+', ' ', 'g')))
|
||||||
|
;
|
||||||
|
|
||||||
|
update imported_contact
|
||||||
|
set contact_id = tax_details.contact_id
|
||||||
|
from contact_tax_details as tax_details
|
||||||
|
join contact using (contact_id)
|
||||||
|
where contact.company_id = end_import_contacts.company_id
|
||||||
|
and tax_details.vatin::text = imported_contact.country_code || imported_contact.vatin
|
||||||
|
;
|
||||||
|
|
||||||
|
update imported_contact
|
||||||
|
set contact_id = nextval('contact_contact_id_seq'::regclass)
|
||||||
|
where length(trim(name)) > 1
|
||||||
|
and contact_id is null
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact (contact_id, company_id, name, tags)
|
||||||
|
select contact_id, end_import_contacts.company_id, name, string_to_array(tags, ' ')::tag_name[]
|
||||||
|
from imported_contact
|
||||||
|
where contact_id is not null
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set tags = array_cat(contact.tags, excluded.tags)
|
||||||
|
;
|
||||||
|
|
||||||
|
-- TODO: use pg_input_is_valid with PostgreSQL 16
|
||||||
|
create or replace function pg_temp.input_is_valid(input text, typename text) returns bool as
|
||||||
|
$func$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
execute format('select %L::%s', input, typename);
|
||||||
|
return true;
|
||||||
|
exception when others then
|
||||||
|
return false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
$func$
|
||||||
|
language plpgsql
|
||||||
|
immutable;
|
||||||
|
|
||||||
|
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||||
|
select contact_id, imported_contact.name, (country_code || vatin)::vatin, address, city, province, postal_code, country_code
|
||||||
|
from imported_contact
|
||||||
|
join country using (country_code)
|
||||||
|
where contact_id is not null
|
||||||
|
and length(address) > 1
|
||||||
|
and length(city) > 1
|
||||||
|
and length(province) > 1
|
||||||
|
and postal_code ~ postal_code_regex
|
||||||
|
and pg_temp.input_is_valid(country_code || vatin, 'vatin')
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set business_name = excluded.business_name
|
||||||
|
, vatin = excluded.vatin
|
||||||
|
, address = excluded.address
|
||||||
|
, city = excluded.city
|
||||||
|
, province = excluded.province
|
||||||
|
, postal_code = excluded.postal_code
|
||||||
|
, country_code = excluded.country_code
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_email (contact_id, email)
|
||||||
|
select contact_id, email::email
|
||||||
|
from imported_contact
|
||||||
|
where contact_id is not null
|
||||||
|
and pg_temp.input_is_valid(email, 'email')
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set email = excluded.email
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_web (contact_id, uri)
|
||||||
|
select contact_id, web::uri
|
||||||
|
from imported_contact
|
||||||
|
where contact_id is not null
|
||||||
|
and pg_temp.input_is_valid(web, 'uri')
|
||||||
|
and length(web) > 1
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set uri = excluded.uri
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_iban (contact_id, iban)
|
||||||
|
select contact_id, iban::iban
|
||||||
|
from imported_contact
|
||||||
|
where contact_id is not null
|
||||||
|
and pg_temp.input_is_valid(iban, 'iban')
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set iban = excluded.iban
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_swift (contact_id, bic)
|
||||||
|
select contact_id, bic::bic
|
||||||
|
from imported_contact
|
||||||
|
where contact_id is not null
|
||||||
|
and pg_temp.input_is_valid(bic, 'bic')
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set bic = excluded.bic
|
||||||
|
;
|
||||||
|
|
||||||
|
-- TODO: use pg_input_is_valid with PostgreSQL 16
|
||||||
|
create or replace function pg_temp.phone_is_valid(phone text, country text) returns bool as
|
||||||
|
$func$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
perform parse_packed_phone_number(phone, country);
|
||||||
|
return true;
|
||||||
|
exception when others then
|
||||||
|
return false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
$func$
|
||||||
|
language plpgsql
|
||||||
|
immutable;
|
||||||
|
|
||||||
|
insert into contact_phone (contact_id, phone)
|
||||||
|
select contact_id, parse_packed_phone_number(phone, case when country_code = '' then 'ES' else country_code end)
|
||||||
|
from imported_contact
|
||||||
|
where contact_id is not null
|
||||||
|
and pg_temp.phone_is_valid(phone, case when country_code = '' then 'ES' else country_code end)
|
||||||
|
on conflict (contact_id) do update
|
||||||
|
set phone = excluded.phone
|
||||||
|
;
|
||||||
|
|
||||||
|
select count(*) from imported_contact where contact_id is not null into imported;
|
||||||
|
return imported;
|
||||||
|
|
||||||
|
drop table imported_contact;
|
||||||
|
end
|
||||||
|
$$
|
||||||
|
language plpgsql;
|
||||||
|
|
||||||
|
revoke execute on function end_import_contacts(integer) from public;
|
||||||
|
grant execute on function end_import_contacts(integer) to invoicer;
|
||||||
|
grant execute on function end_import_contacts(integer) to admin;
|
||||||
|
|
||||||
|
commit;
|
1
go.mod
1
go.mod
|
@ -7,6 +7,7 @@ require (
|
||||||
github.com/jackc/pgx/v4 v4.15.0
|
github.com/jackc/pgx/v4 v4.15.0
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/leonelquinteros/gotext v1.5.0
|
github.com/leonelquinteros/gotext v1.5.0
|
||||||
|
github.com/tealeg/xlsx v0.0.0-20181024002044-dbf71b6a931e
|
||||||
golang.org/x/text v0.7.0
|
golang.org/x/text v0.7.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -70,9 +70,11 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/leonelquinteros/gotext v1.5.0 h1:ODY7LzLpZWWSJdAHnzhreOr6cwLXTAmc914FOauSkBM=
|
github.com/leonelquinteros/gotext v1.5.0 h1:ODY7LzLpZWWSJdAHnzhreOr6cwLXTAmc914FOauSkBM=
|
||||||
github.com/leonelquinteros/gotext v1.5.0/go.mod h1:OCiUVHuhP9LGFBQ1oAmdtNCHJCiHiQA8lf4nAifHkr0=
|
github.com/leonelquinteros/gotext v1.5.0/go.mod h1:OCiUVHuhP9LGFBQ1oAmdtNCHJCiHiQA8lf4nAifHkr0=
|
||||||
|
@ -112,6 +114,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/tealeg/xlsx v0.0.0-20181024002044-dbf71b6a931e h1:0AoAjM/7iqEZwTsWhk3nm9+H5mocFnh6dCGUaIOSTDQ=
|
||||||
|
github.com/tealeg/xlsx v0.0.0-20181024002044-dbf71b6a931e/go.mod h1:uxu5UY2ovkuRPWKQ8Q7JG0JbSivrISjdPzZQKeo74mA=
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
@ -184,6 +188,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/tealeg/xlsx"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -41,8 +42,12 @@ func IndexContacts(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||||
func GetContactForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
func GetContactForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||||
locale := getLocale(r)
|
locale := getLocale(r)
|
||||||
conn := getConn(r)
|
conn := getConn(r)
|
||||||
form := newContactForm(r.Context(), conn, locale)
|
|
||||||
slug := params[0].Value
|
slug := params[0].Value
|
||||||
|
if slug == "import" {
|
||||||
|
ServeImportPage(w, r, params)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
form := newContactForm(r.Context(), conn, locale)
|
||||||
if slug == "new" {
|
if slug == "new" {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
mustRenderNewContactForm(w, r, form)
|
mustRenderNewContactForm(w, r, form)
|
||||||
|
@ -501,3 +506,92 @@ func HandleUpdateContactTags(w http.ResponseWriter, r *http.Request, params http
|
||||||
}
|
}
|
||||||
mustRenderStandaloneTemplate(w, r, "tags/view.gohtml", form)
|
mustRenderStandaloneTemplate(w, r, "tags/view.gohtml", form)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ServeImportPage(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
|
form := newContactImportForm(getLocale(r), getCompany(r))
|
||||||
|
mustRenderMainTemplate(w, r, "contacts/import.gohtml", form)
|
||||||
|
}
|
||||||
|
|
||||||
|
type contactImportForm struct {
|
||||||
|
locale *Locale
|
||||||
|
company *Company
|
||||||
|
File *FileField
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContactImportForm(locale *Locale, company *Company) *contactImportForm {
|
||||||
|
return &contactImportForm{
|
||||||
|
locale: locale,
|
||||||
|
company: company,
|
||||||
|
File: &FileField{
|
||||||
|
Name: "file",
|
||||||
|
Label: pgettext("input", "Holded Excel file", locale),
|
||||||
|
MaxSize: 1 << 20,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (form *contactImportForm) Parse(r *http.Request) error {
|
||||||
|
if err := r.ParseMultipartForm(form.File.MaxSize); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := form.File.FillValue(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleImportContacts(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
|
locale := getLocale(r)
|
||||||
|
company := mustGetCompany(r)
|
||||||
|
form := newContactImportForm(locale, company)
|
||||||
|
if err := form.Parse(r); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := verifyCsrfTokenValid(r); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
workbook, err := xlsx.OpenBinary(form.File.Content)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
conn := getConn(r)
|
||||||
|
tx := conn.MustBegin(r.Context())
|
||||||
|
defer tx.MustRollback(r.Context())
|
||||||
|
relation := tx.MustGetText(r.Context(), "select begin_import_contacts()")
|
||||||
|
columns := []string{"name", "vatin", "email", "phone", "address", "city", "postal_code", "province", "country_code", "iban", "bic", "tags"}
|
||||||
|
for _, sheet := range workbook.Sheets {
|
||||||
|
tx.MustCopyFrom(r.Context(), relation, columns, len(sheet.Rows)-4, func(idx int) ([]interface{}, error) {
|
||||||
|
row := sheet.Rows[idx+4]
|
||||||
|
var values []interface{}
|
||||||
|
if len(row.Cells) < 23 {
|
||||||
|
values = []interface{}{"", "", "", "", "", "", "", "", "", "", "", ""}
|
||||||
|
} else {
|
||||||
|
phone := row.Cells[5].String() // mobile
|
||||||
|
if phone == "" {
|
||||||
|
phone = row.Cells[4].String() // landline
|
||||||
|
}
|
||||||
|
values = []interface{}{
|
||||||
|
row.Cells[1].String(),
|
||||||
|
row.Cells[2].String(),
|
||||||
|
row.Cells[3].String(),
|
||||||
|
phone,
|
||||||
|
row.Cells[6].String(),
|
||||||
|
row.Cells[7].String(),
|
||||||
|
row.Cells[8].String(),
|
||||||
|
row.Cells[9].String(),
|
||||||
|
row.Cells[11].String(),
|
||||||
|
row.Cells[19].String(),
|
||||||
|
row.Cells[20].String(),
|
||||||
|
row.Cells[22].String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
tx.MustExec(r.Context(), "select end_import_contacts($1)", company.Id)
|
||||||
|
tx.MustCommit(r.Context())
|
||||||
|
htmxRedirect(w, r, companyURI(company, "/contacts"))
|
||||||
|
}
|
||||||
|
|
12
pkg/db.go
12
pkg/db.go
|
@ -143,6 +143,14 @@ func (tx *Tx) MustExec(ctx context.Context, sql string, args ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) MustGetText(ctx context.Context, sql string, args ...interface{}) string {
|
||||||
|
var result string
|
||||||
|
if err := tx.QueryRow(ctx, sql, args...).Scan(&result); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *Tx) MustGetInteger(ctx context.Context, sql string, args ...interface{}) int {
|
func (tx *Tx) MustGetInteger(ctx context.Context, sql string, args ...interface{}) int {
|
||||||
var result int
|
var result int
|
||||||
if err := tx.QueryRow(ctx, sql, args...).Scan(&result); err != nil {
|
if err := tx.QueryRow(ctx, sql, args...).Scan(&result); err != nil {
|
||||||
|
@ -159,8 +167,8 @@ func (tx *Tx) MustGetIntegerOrDefault(ctx context.Context, def int, sql string,
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Tx) MustCopyFrom(ctx context.Context, tableName string, columns []string, rows [][]interface{}) int64 {
|
func (tx *Tx) MustCopyFrom(ctx context.Context, tableName string, columns []string, length int, next func(int) ([]interface{}, error)) int64 {
|
||||||
copied, err := tx.CopyFrom(ctx, pgx.Identifier{tableName}, columns, pgx.CopyFromRows(rows))
|
copied, err := tx.CopyFrom(ctx, pgx.Identifier{tableName}, columns, pgx.CopyFromSlice(length, next))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,14 @@ func IndexExpenses(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
page := &expensesIndexPage{
|
page := &expensesIndexPage{
|
||||||
Expenses: mustCollectExpenseEntries(r.Context(), conn, company, filters),
|
Expenses: mustCollectExpenseEntries(r.Context(), conn, filters),
|
||||||
TotalAmount: mustComputeExpensesTotalAmount(r.Context(), conn, filters),
|
TotalAmount: mustComputeExpensesTotalAmount(r.Context(), conn, filters),
|
||||||
Filters: filters,
|
Filters: filters,
|
||||||
}
|
}
|
||||||
mustRenderMainTemplate(w, r, "expenses/index.gohtml", page)
|
mustRenderMainTemplate(w, r, "expenses/index.gohtml", page)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustCollectExpenseEntries(ctx context.Context, conn *Conn, company *Company, filters *expenseFilterForm) []*ExpenseEntry {
|
func mustCollectExpenseEntries(ctx context.Context, conn *Conn, filters *expenseFilterForm) []*ExpenseEntry {
|
||||||
where, args := filters.BuildQuery(nil)
|
where, args := filters.BuildQuery(nil)
|
||||||
rows := conn.MustQuery(ctx, fmt.Sprintf(`
|
rows := conn.MustQuery(ctx, fmt.Sprintf(`
|
||||||
select expense.slug
|
select expense.slug
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jackc/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -325,6 +325,7 @@ type FileField struct {
|
||||||
OriginalFileName string
|
OriginalFileName string
|
||||||
ContentType string
|
ContentType string
|
||||||
Content []byte
|
Content []byte
|
||||||
|
Required bool
|
||||||
Errors []error
|
Errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +338,7 @@ func (field *FileField) FillValue(r *http.Request) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
field.Content, err = ioutil.ReadAll(file)
|
field.Content, err = io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ func NewRouter(db *Db) http.Handler {
|
||||||
companyRouter.DELETE("/payment-method/:paymentMethodId", HandleDeletePaymentMethod)
|
companyRouter.DELETE("/payment-method/:paymentMethodId", HandleDeletePaymentMethod)
|
||||||
companyRouter.GET("/contacts", IndexContacts)
|
companyRouter.GET("/contacts", IndexContacts)
|
||||||
companyRouter.POST("/contacts", HandleAddContact)
|
companyRouter.POST("/contacts", HandleAddContact)
|
||||||
|
companyRouter.POST("/contacts/import", HandleImportContacts)
|
||||||
companyRouter.GET("/contacts/:slug", GetContactForm)
|
companyRouter.GET("/contacts/:slug", GetContactForm)
|
||||||
companyRouter.PUT("/contacts/:slug", HandleUpdateContact)
|
companyRouter.PUT("/contacts/:slug", HandleUpdateContact)
|
||||||
companyRouter.PUT("/contacts/:slug/tags", HandleUpdateContactTags)
|
companyRouter.PUT("/contacts/:slug/tags", HandleUpdateContactTags)
|
||||||
|
|
119
po/ca.po
119
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: numerus\n"
|
"Project-Id-Version: numerus\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-07-02 01:58+0200\n"
|
"POT-Creation-Date: 2023-07-02 23:47+0200\n"
|
||||||
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||||
|
@ -30,10 +30,11 @@ msgstr "Afegeix productes a la factura"
|
||||||
#: web/template/quotes/index.gohtml:9 web/template/quotes/view.gohtml:9
|
#: web/template/quotes/index.gohtml:9 web/template/quotes/view.gohtml:9
|
||||||
#: web/template/quotes/edit.gohtml:9 web/template/contacts/new.gohtml:9
|
#: web/template/quotes/edit.gohtml:9 web/template/contacts/new.gohtml:9
|
||||||
#: web/template/contacts/index.gohtml:9 web/template/contacts/edit.gohtml:10
|
#: web/template/contacts/index.gohtml:9 web/template/contacts/edit.gohtml:10
|
||||||
#: web/template/profile.gohtml:9 web/template/expenses/new.gohtml:10
|
#: web/template/contacts/import.gohtml:8 web/template/profile.gohtml:9
|
||||||
#: web/template/expenses/index.gohtml:10 web/template/expenses/edit.gohtml:10
|
#: web/template/expenses/new.gohtml:10 web/template/expenses/index.gohtml:10
|
||||||
#: web/template/tax-details.gohtml:9 web/template/products/new.gohtml:9
|
#: web/template/expenses/edit.gohtml:10 web/template/tax-details.gohtml:9
|
||||||
#: web/template/products/index.gohtml:9 web/template/products/edit.gohtml:10
|
#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:9
|
||||||
|
#: web/template/products/edit.gohtml:10
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Inici"
|
msgstr "Inici"
|
||||||
|
@ -134,7 +135,7 @@ msgid "New invoice"
|
||||||
msgstr "Nova factura"
|
msgstr "Nova factura"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
|
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
|
||||||
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:34
|
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:36
|
||||||
#: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
|
#: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
|
@ -167,7 +168,7 @@ msgid "Status"
|
||||||
msgstr "Estat"
|
msgstr "Estat"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
|
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
|
||||||
#: web/template/contacts/index.gohtml:43 web/template/expenses/index.gohtml:46
|
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:46
|
||||||
#: web/template/products/index.gohtml:41
|
#: web/template/products/index.gohtml:41
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
|
@ -186,7 +187,7 @@ msgid "Download"
|
||||||
msgstr "Descàrrega"
|
msgstr "Descàrrega"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
|
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
|
||||||
#: web/template/contacts/index.gohtml:44 web/template/expenses/index.gohtml:49
|
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:49
|
||||||
#: web/template/products/index.gohtml:43
|
#: web/template/products/index.gohtml:43
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
|
@ -199,7 +200,7 @@ msgstr "Selecciona factura %v"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:119 web/template/invoices/view.gohtml:19
|
#: web/template/invoices/index.gohtml:119 web/template/invoices/view.gohtml:19
|
||||||
#: web/template/quotes/index.gohtml:119 web/template/quotes/view.gohtml:22
|
#: web/template/quotes/index.gohtml:119 web/template/quotes/view.gohtml:22
|
||||||
#: web/template/contacts/index.gohtml:74 web/template/expenses/index.gohtml:88
|
#: web/template/contacts/index.gohtml:76 web/template/expenses/index.gohtml:88
|
||||||
#: web/template/products/index.gohtml:72
|
#: web/template/products/index.gohtml:72
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
|
@ -444,31 +445,37 @@ msgstr "Nou contacte"
|
||||||
|
|
||||||
#: web/template/contacts/new.gohtml:10 web/template/contacts/index.gohtml:2
|
#: web/template/contacts/new.gohtml:10 web/template/contacts/index.gohtml:2
|
||||||
#: web/template/contacts/index.gohtml:10 web/template/contacts/edit.gohtml:11
|
#: web/template/contacts/index.gohtml:10 web/template/contacts/edit.gohtml:11
|
||||||
|
#: web/template/contacts/import.gohtml:9
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Contacts"
|
msgid "Contacts"
|
||||||
msgstr "Contactes"
|
msgstr "Contactes"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:15
|
#: web/template/contacts/index.gohtml:15
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
|
msgid "Import"
|
||||||
|
msgstr "Importa"
|
||||||
|
|
||||||
|
#: web/template/contacts/index.gohtml:17
|
||||||
|
msgctxt "action"
|
||||||
msgid "New contact"
|
msgid "New contact"
|
||||||
msgstr "Nou contacte"
|
msgstr "Nou contacte"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:40 web/template/expenses/index.gohtml:43
|
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:43
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Contact"
|
msgid "Contact"
|
||||||
msgstr "Contacte"
|
msgstr "Contacte"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:41
|
#: web/template/contacts/index.gohtml:43
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correu-e"
|
msgstr "Correu-e"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:42
|
#: web/template/contacts/index.gohtml:44
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Telèfon"
|
msgstr "Telèfon"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:84
|
#: web/template/contacts/index.gohtml:86
|
||||||
msgid "No contacts added yet."
|
msgid "No contacts added yet."
|
||||||
msgstr "No hi ha cap contacte."
|
msgstr "No hi ha cap contacte."
|
||||||
|
|
||||||
|
@ -477,6 +484,11 @@ msgctxt "title"
|
||||||
msgid "Edit Contact “%s”"
|
msgid "Edit Contact “%s”"
|
||||||
msgstr "Edició del contacte «%s»"
|
msgstr "Edició del contacte «%s»"
|
||||||
|
|
||||||
|
#: web/template/contacts/import.gohtml:2 web/template/contacts/import.gohtml:10
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Import Contacts"
|
||||||
|
msgstr "Importació de contactes"
|
||||||
|
|
||||||
#: web/template/login.gohtml:2 web/template/login.gohtml:15
|
#: web/template/login.gohtml:2 web/template/login.gohtml:15
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
|
@ -648,7 +660,7 @@ msgctxt "title"
|
||||||
msgid "Edit Product “%s”"
|
msgid "Edit Product “%s”"
|
||||||
msgstr "Edició del producte «%s»"
|
msgstr "Edició del producte «%s»"
|
||||||
|
|
||||||
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:257
|
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:262
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correu-e"
|
msgstr "Correu-e"
|
||||||
|
@ -662,7 +674,7 @@ msgstr "Contrasenya"
|
||||||
msgid "Email can not be empty."
|
msgid "Email can not be empty."
|
||||||
msgstr "No podeu deixar el correu-e en blanc."
|
msgstr "No podeu deixar el correu-e en blanc."
|
||||||
|
|
||||||
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:399
|
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:404
|
||||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||||
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
|
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
|
||||||
|
|
||||||
|
@ -675,44 +687,44 @@ msgid "Invalid user or password."
|
||||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||||
|
|
||||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
||||||
#: pkg/contacts.go:135 pkg/contacts.go:243
|
#: pkg/contacts.go:140 pkg/contacts.go:248
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
||||||
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
||||||
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:140
|
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:145
|
||||||
#: pkg/contacts.go:343
|
#: pkg/contacts.go:348
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetes"
|
msgstr "Etiquetes"
|
||||||
|
|
||||||
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
|
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
|
||||||
#: pkg/contacts.go:144
|
#: pkg/contacts.go:149
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Tags Condition"
|
msgid "Tags Condition"
|
||||||
msgstr "Condició de les etiquetes"
|
msgstr "Condició de les etiquetes"
|
||||||
|
|
||||||
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
|
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
|
||||||
#: pkg/contacts.go:148
|
#: pkg/contacts.go:153
|
||||||
msgctxt "tag condition"
|
msgctxt "tag condition"
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Totes"
|
msgstr "Totes"
|
||||||
|
|
||||||
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
|
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
|
||||||
#: pkg/contacts.go:149
|
#: pkg/contacts.go:154
|
||||||
msgid "Invoices must have all the specified labels."
|
msgid "Invoices must have all the specified labels."
|
||||||
msgstr "Les factures han de tenir totes les etiquetes."
|
msgstr "Les factures han de tenir totes les etiquetes."
|
||||||
|
|
||||||
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
|
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
|
||||||
#: pkg/contacts.go:153
|
#: pkg/contacts.go:158
|
||||||
msgctxt "tag condition"
|
msgctxt "tag condition"
|
||||||
msgid "Any"
|
msgid "Any"
|
||||||
msgstr "Qualsevol"
|
msgstr "Qualsevol"
|
||||||
|
|
||||||
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
|
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
|
||||||
#: pkg/contacts.go:154
|
#: pkg/contacts.go:159
|
||||||
msgid "Invoices must have at least one of the specified labels."
|
msgid "Invoices must have at least one of the specified labels."
|
||||||
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
|
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
|
||||||
|
|
||||||
|
@ -732,7 +744,7 @@ msgid "Taxes"
|
||||||
msgstr "Imposts"
|
msgstr "Imposts"
|
||||||
|
|
||||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
||||||
#: pkg/contacts.go:392
|
#: pkg/contacts.go:397
|
||||||
msgid "Name can not be empty."
|
msgid "Name can not be empty."
|
||||||
msgstr "No podeu deixar el nom en blanc."
|
msgstr "No podeu deixar el nom en blanc."
|
||||||
|
|
||||||
|
@ -759,47 +771,47 @@ msgctxt "input"
|
||||||
msgid "Trade name"
|
msgid "Trade name"
|
||||||
msgstr "Nom comercial"
|
msgstr "Nom comercial"
|
||||||
|
|
||||||
#: pkg/company.go:118 pkg/contacts.go:249
|
#: pkg/company.go:118 pkg/contacts.go:254
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Telèfon"
|
msgstr "Telèfon"
|
||||||
|
|
||||||
#: pkg/company.go:136 pkg/contacts.go:265
|
#: pkg/company.go:136 pkg/contacts.go:270
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Web"
|
msgid "Web"
|
||||||
msgstr "Web"
|
msgstr "Web"
|
||||||
|
|
||||||
#: pkg/company.go:144 pkg/contacts.go:277
|
#: pkg/company.go:144 pkg/contacts.go:282
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Business name"
|
msgid "Business name"
|
||||||
msgstr "Nom i cognoms"
|
msgstr "Nom i cognoms"
|
||||||
|
|
||||||
#: pkg/company.go:154 pkg/contacts.go:287
|
#: pkg/company.go:154 pkg/contacts.go:292
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "VAT number"
|
msgid "VAT number"
|
||||||
msgstr "DNI / NIF"
|
msgstr "DNI / NIF"
|
||||||
|
|
||||||
#: pkg/company.go:160 pkg/contacts.go:293
|
#: pkg/company.go:160 pkg/contacts.go:298
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Adreça"
|
msgstr "Adreça"
|
||||||
|
|
||||||
#: pkg/company.go:169 pkg/contacts.go:302
|
#: pkg/company.go:169 pkg/contacts.go:307
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "City"
|
msgid "City"
|
||||||
msgstr "Població"
|
msgstr "Població"
|
||||||
|
|
||||||
#: pkg/company.go:175 pkg/contacts.go:308
|
#: pkg/company.go:175 pkg/contacts.go:313
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Province"
|
msgid "Province"
|
||||||
msgstr "Província"
|
msgstr "Província"
|
||||||
|
|
||||||
#: pkg/company.go:181 pkg/contacts.go:314
|
#: pkg/company.go:181 pkg/contacts.go:319
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Postal code"
|
msgid "Postal code"
|
||||||
msgstr "Codi postal"
|
msgstr "Codi postal"
|
||||||
|
|
||||||
#: pkg/company.go:190 pkg/contacts.go:323
|
#: pkg/company.go:190 pkg/contacts.go:328
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Country"
|
msgid "Country"
|
||||||
msgstr "País"
|
msgstr "País"
|
||||||
|
@ -834,23 +846,23 @@ msgctxt "input"
|
||||||
msgid "Legal disclaimer"
|
msgid "Legal disclaimer"
|
||||||
msgstr "Nota legal"
|
msgstr "Nota legal"
|
||||||
|
|
||||||
#: pkg/company.go:271 pkg/contacts.go:375
|
#: pkg/company.go:271 pkg/contacts.go:380
|
||||||
msgid "Selected country is not valid."
|
msgid "Selected country is not valid."
|
||||||
msgstr "Heu seleccionat un país que no és vàlid."
|
msgstr "Heu seleccionat un país que no és vàlid."
|
||||||
|
|
||||||
#: pkg/company.go:275 pkg/contacts.go:378
|
#: pkg/company.go:275 pkg/contacts.go:383
|
||||||
msgid "Business name can not be empty."
|
msgid "Business name can not be empty."
|
||||||
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:276 pkg/contacts.go:379
|
#: pkg/company.go:276 pkg/contacts.go:384
|
||||||
msgid "Business name must have at least two letters."
|
msgid "Business name must have at least two letters."
|
||||||
msgstr "Nom i cognoms han de tenir com a mínim dues lletres."
|
msgstr "Nom i cognoms han de tenir com a mínim dues lletres."
|
||||||
|
|
||||||
#: pkg/company.go:277 pkg/contacts.go:380
|
#: pkg/company.go:277 pkg/contacts.go:385
|
||||||
msgid "VAT number can not be empty."
|
msgid "VAT number can not be empty."
|
||||||
msgstr "No podeu deixar el DNI o NIF en blanc."
|
msgstr "No podeu deixar el DNI o NIF en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:278 pkg/contacts.go:381
|
#: pkg/company.go:278 pkg/contacts.go:386
|
||||||
msgid "This value is not a valid VAT number."
|
msgid "This value is not a valid VAT number."
|
||||||
msgstr "Aquest valor no és un DNI o NIF vàlid."
|
msgstr "Aquest valor no és un DNI o NIF vàlid."
|
||||||
|
|
||||||
|
@ -858,31 +870,31 @@ msgstr "Aquest valor no és un DNI o NIF vàlid."
|
||||||
msgid "Phone can not be empty."
|
msgid "Phone can not be empty."
|
||||||
msgstr "No podeu deixar el telèfon en blanc."
|
msgstr "No podeu deixar el telèfon en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:281 pkg/contacts.go:396
|
#: pkg/company.go:281 pkg/contacts.go:401
|
||||||
msgid "This value is not a valid phone number."
|
msgid "This value is not a valid phone number."
|
||||||
msgstr "Aquest valor no és un telèfon vàlid."
|
msgstr "Aquest valor no és un telèfon vàlid."
|
||||||
|
|
||||||
#: pkg/company.go:287 pkg/contacts.go:402
|
#: pkg/company.go:287 pkg/contacts.go:407
|
||||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||||
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
|
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
|
||||||
|
|
||||||
#: pkg/company.go:289 pkg/contacts.go:383
|
#: pkg/company.go:289 pkg/contacts.go:388
|
||||||
msgid "Address can not be empty."
|
msgid "Address can not be empty."
|
||||||
msgstr "No podeu deixar l’adreça en blanc."
|
msgstr "No podeu deixar l’adreça en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:290 pkg/contacts.go:384
|
#: pkg/company.go:290 pkg/contacts.go:389
|
||||||
msgid "City can not be empty."
|
msgid "City can not be empty."
|
||||||
msgstr "No podeu deixar la població en blanc."
|
msgstr "No podeu deixar la població en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:291 pkg/contacts.go:385
|
#: pkg/company.go:291 pkg/contacts.go:390
|
||||||
msgid "Province can not be empty."
|
msgid "Province can not be empty."
|
||||||
msgstr "No podeu deixar la província en blanc."
|
msgstr "No podeu deixar la província en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:292 pkg/contacts.go:387
|
#: pkg/company.go:292 pkg/contacts.go:392
|
||||||
msgid "Postal code can not be empty."
|
msgid "Postal code can not be empty."
|
||||||
msgstr "No podeu deixar el codi postal en blanc."
|
msgstr "No podeu deixar el codi postal en blanc."
|
||||||
|
|
||||||
#: pkg/company.go:293 pkg/contacts.go:388
|
#: pkg/company.go:293 pkg/contacts.go:393
|
||||||
msgid "This value is not a valid postal code."
|
msgid "This value is not a valid postal code."
|
||||||
msgstr "Aquest valor no és un codi postal vàlid."
|
msgstr "Aquest valor no és un codi postal vàlid."
|
||||||
|
|
||||||
|
@ -1248,33 +1260,38 @@ msgstr "DD/MM/YYYY"
|
||||||
msgid "Invoice product ID must be a number greater than zero."
|
msgid "Invoice product ID must be a number greater than zero."
|
||||||
msgstr "L’ID del producte de factura ha de ser un número major a zero."
|
msgstr "L’ID del producte de factura ha de ser un número major a zero."
|
||||||
|
|
||||||
#: pkg/contacts.go:273
|
#: pkg/contacts.go:278
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Need to input tax details"
|
msgid "Need to input tax details"
|
||||||
msgstr "Necessito poder facturar aquest contacte"
|
msgstr "Necessito poder facturar aquest contacte"
|
||||||
|
|
||||||
#: pkg/contacts.go:333
|
#: pkg/contacts.go:338
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "IBAN"
|
msgid "IBAN"
|
||||||
msgstr "IBAN"
|
msgstr "IBAN"
|
||||||
|
|
||||||
#: pkg/contacts.go:338
|
#: pkg/contacts.go:343
|
||||||
msgctxt "bic"
|
msgctxt "bic"
|
||||||
msgid "BIC"
|
msgid "BIC"
|
||||||
msgstr "BIC"
|
msgstr "BIC"
|
||||||
|
|
||||||
#: pkg/contacts.go:393
|
#: pkg/contacts.go:398
|
||||||
msgid "Name must have at least two letters."
|
msgid "Name must have at least two letters."
|
||||||
msgstr "El nom ha de tenir com a mínim dues lletres."
|
msgstr "El nom ha de tenir com a mínim dues lletres."
|
||||||
|
|
||||||
#: pkg/contacts.go:405
|
#: pkg/contacts.go:410
|
||||||
msgid "This values is not a valid IBAN."
|
msgid "This values is not a valid IBAN."
|
||||||
msgstr "Aquest valor no és un IBAN vàlid."
|
msgstr "Aquest valor no és un IBAN vàlid."
|
||||||
|
|
||||||
#: pkg/contacts.go:408
|
#: pkg/contacts.go:413
|
||||||
msgid "This values is not a valid BIC."
|
msgid "This values is not a valid BIC."
|
||||||
msgstr "Aquest valor no és un BIC vàlid."
|
msgstr "Aquest valor no és un BIC vàlid."
|
||||||
|
|
||||||
|
#: pkg/contacts.go:527
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Holded Excel file"
|
||||||
|
msgstr "Fitxer Excel del Holded"
|
||||||
|
|
||||||
#~ msgctxt "action"
|
#~ msgctxt "action"
|
||||||
#~ msgid "Update contact"
|
#~ msgid "Update contact"
|
||||||
#~ msgstr "Actualitza contacte"
|
#~ msgstr "Actualitza contacte"
|
||||||
|
|
119
po/es.po
119
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: numerus\n"
|
"Project-Id-Version: numerus\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-07-02 01:58+0200\n"
|
"POT-Creation-Date: 2023-07-02 23:47+0200\n"
|
||||||
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||||
|
@ -30,10 +30,11 @@ msgstr "Añadir productos a la factura"
|
||||||
#: web/template/quotes/index.gohtml:9 web/template/quotes/view.gohtml:9
|
#: web/template/quotes/index.gohtml:9 web/template/quotes/view.gohtml:9
|
||||||
#: web/template/quotes/edit.gohtml:9 web/template/contacts/new.gohtml:9
|
#: web/template/quotes/edit.gohtml:9 web/template/contacts/new.gohtml:9
|
||||||
#: web/template/contacts/index.gohtml:9 web/template/contacts/edit.gohtml:10
|
#: web/template/contacts/index.gohtml:9 web/template/contacts/edit.gohtml:10
|
||||||
#: web/template/profile.gohtml:9 web/template/expenses/new.gohtml:10
|
#: web/template/contacts/import.gohtml:8 web/template/profile.gohtml:9
|
||||||
#: web/template/expenses/index.gohtml:10 web/template/expenses/edit.gohtml:10
|
#: web/template/expenses/new.gohtml:10 web/template/expenses/index.gohtml:10
|
||||||
#: web/template/tax-details.gohtml:9 web/template/products/new.gohtml:9
|
#: web/template/expenses/edit.gohtml:10 web/template/tax-details.gohtml:9
|
||||||
#: web/template/products/index.gohtml:9 web/template/products/edit.gohtml:10
|
#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:9
|
||||||
|
#: web/template/products/edit.gohtml:10
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Inicio"
|
msgstr "Inicio"
|
||||||
|
@ -134,7 +135,7 @@ msgid "New invoice"
|
||||||
msgstr "Nueva factura"
|
msgstr "Nueva factura"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
|
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
|
||||||
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:34
|
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:36
|
||||||
#: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
|
#: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
|
@ -167,7 +168,7 @@ msgid "Status"
|
||||||
msgstr "Estado"
|
msgstr "Estado"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
|
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
|
||||||
#: web/template/contacts/index.gohtml:43 web/template/expenses/index.gohtml:46
|
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:46
|
||||||
#: web/template/products/index.gohtml:41
|
#: web/template/products/index.gohtml:41
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
|
@ -186,7 +187,7 @@ msgid "Download"
|
||||||
msgstr "Descargar"
|
msgstr "Descargar"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
|
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
|
||||||
#: web/template/contacts/index.gohtml:44 web/template/expenses/index.gohtml:49
|
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:49
|
||||||
#: web/template/products/index.gohtml:43
|
#: web/template/products/index.gohtml:43
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
|
@ -199,7 +200,7 @@ msgstr "Seleccionar factura %v"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:119 web/template/invoices/view.gohtml:19
|
#: web/template/invoices/index.gohtml:119 web/template/invoices/view.gohtml:19
|
||||||
#: web/template/quotes/index.gohtml:119 web/template/quotes/view.gohtml:22
|
#: web/template/quotes/index.gohtml:119 web/template/quotes/view.gohtml:22
|
||||||
#: web/template/contacts/index.gohtml:74 web/template/expenses/index.gohtml:88
|
#: web/template/contacts/index.gohtml:76 web/template/expenses/index.gohtml:88
|
||||||
#: web/template/products/index.gohtml:72
|
#: web/template/products/index.gohtml:72
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
|
@ -444,31 +445,37 @@ msgstr "Nuevo contacto"
|
||||||
|
|
||||||
#: web/template/contacts/new.gohtml:10 web/template/contacts/index.gohtml:2
|
#: web/template/contacts/new.gohtml:10 web/template/contacts/index.gohtml:2
|
||||||
#: web/template/contacts/index.gohtml:10 web/template/contacts/edit.gohtml:11
|
#: web/template/contacts/index.gohtml:10 web/template/contacts/edit.gohtml:11
|
||||||
|
#: web/template/contacts/import.gohtml:9
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Contacts"
|
msgid "Contacts"
|
||||||
msgstr "Contactos"
|
msgstr "Contactos"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:15
|
#: web/template/contacts/index.gohtml:15
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
|
msgid "Import"
|
||||||
|
msgstr "Importar"
|
||||||
|
|
||||||
|
#: web/template/contacts/index.gohtml:17
|
||||||
|
msgctxt "action"
|
||||||
msgid "New contact"
|
msgid "New contact"
|
||||||
msgstr "Nuevo contacto"
|
msgstr "Nuevo contacto"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:40 web/template/expenses/index.gohtml:43
|
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:43
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Contact"
|
msgid "Contact"
|
||||||
msgstr "Contacto"
|
msgstr "Contacto"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:41
|
#: web/template/contacts/index.gohtml:43
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correo-e"
|
msgstr "Correo-e"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:42
|
#: web/template/contacts/index.gohtml:44
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Teléfono"
|
msgstr "Teléfono"
|
||||||
|
|
||||||
#: web/template/contacts/index.gohtml:84
|
#: web/template/contacts/index.gohtml:86
|
||||||
msgid "No contacts added yet."
|
msgid "No contacts added yet."
|
||||||
msgstr "No hay contactos."
|
msgstr "No hay contactos."
|
||||||
|
|
||||||
|
@ -477,6 +484,11 @@ msgctxt "title"
|
||||||
msgid "Edit Contact “%s”"
|
msgid "Edit Contact “%s”"
|
||||||
msgstr "Edición del contacto «%s»"
|
msgstr "Edición del contacto «%s»"
|
||||||
|
|
||||||
|
#: web/template/contacts/import.gohtml:2 web/template/contacts/import.gohtml:10
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Import Contacts"
|
||||||
|
msgstr "Importación de contactos"
|
||||||
|
|
||||||
#: web/template/login.gohtml:2 web/template/login.gohtml:15
|
#: web/template/login.gohtml:2 web/template/login.gohtml:15
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
|
@ -648,7 +660,7 @@ msgctxt "title"
|
||||||
msgid "Edit Product “%s”"
|
msgid "Edit Product “%s”"
|
||||||
msgstr "Edición del producto «%s»"
|
msgstr "Edición del producto «%s»"
|
||||||
|
|
||||||
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:257
|
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:262
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correo-e"
|
msgstr "Correo-e"
|
||||||
|
@ -662,7 +674,7 @@ msgstr "Contraseña"
|
||||||
msgid "Email can not be empty."
|
msgid "Email can not be empty."
|
||||||
msgstr "No podéis dejar el correo-e en blanco."
|
msgstr "No podéis dejar el correo-e en blanco."
|
||||||
|
|
||||||
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:399
|
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:404
|
||||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||||
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
|
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
|
||||||
|
|
||||||
|
@ -675,44 +687,44 @@ msgid "Invalid user or password."
|
||||||
msgstr "Nombre de usuario o contraseña inválido."
|
msgstr "Nombre de usuario o contraseña inválido."
|
||||||
|
|
||||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
||||||
#: pkg/contacts.go:135 pkg/contacts.go:243
|
#: pkg/contacts.go:140 pkg/contacts.go:248
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
|
|
||||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
||||||
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
||||||
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:140
|
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:145
|
||||||
#: pkg/contacts.go:343
|
#: pkg/contacts.go:348
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetes"
|
msgstr "Etiquetes"
|
||||||
|
|
||||||
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
|
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
|
||||||
#: pkg/contacts.go:144
|
#: pkg/contacts.go:149
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Tags Condition"
|
msgid "Tags Condition"
|
||||||
msgstr "Condición de las etiquetas"
|
msgstr "Condición de las etiquetas"
|
||||||
|
|
||||||
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
|
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
|
||||||
#: pkg/contacts.go:148
|
#: pkg/contacts.go:153
|
||||||
msgctxt "tag condition"
|
msgctxt "tag condition"
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todas"
|
msgstr "Todas"
|
||||||
|
|
||||||
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
|
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
|
||||||
#: pkg/contacts.go:149
|
#: pkg/contacts.go:154
|
||||||
msgid "Invoices must have all the specified labels."
|
msgid "Invoices must have all the specified labels."
|
||||||
msgstr "Las facturas deben tener todas las etiquetas."
|
msgstr "Las facturas deben tener todas las etiquetas."
|
||||||
|
|
||||||
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
|
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
|
||||||
#: pkg/contacts.go:153
|
#: pkg/contacts.go:158
|
||||||
msgctxt "tag condition"
|
msgctxt "tag condition"
|
||||||
msgid "Any"
|
msgid "Any"
|
||||||
msgstr "Cualquiera"
|
msgstr "Cualquiera"
|
||||||
|
|
||||||
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
|
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
|
||||||
#: pkg/contacts.go:154
|
#: pkg/contacts.go:159
|
||||||
msgid "Invoices must have at least one of the specified labels."
|
msgid "Invoices must have at least one of the specified labels."
|
||||||
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
|
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
|
||||||
|
|
||||||
|
@ -732,7 +744,7 @@ msgid "Taxes"
|
||||||
msgstr "Impuestos"
|
msgstr "Impuestos"
|
||||||
|
|
||||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
||||||
#: pkg/contacts.go:392
|
#: pkg/contacts.go:397
|
||||||
msgid "Name can not be empty."
|
msgid "Name can not be empty."
|
||||||
msgstr "No podéis dejar el nombre en blanco."
|
msgstr "No podéis dejar el nombre en blanco."
|
||||||
|
|
||||||
|
@ -759,47 +771,47 @@ msgctxt "input"
|
||||||
msgid "Trade name"
|
msgid "Trade name"
|
||||||
msgstr "Nombre comercial"
|
msgstr "Nombre comercial"
|
||||||
|
|
||||||
#: pkg/company.go:118 pkg/contacts.go:249
|
#: pkg/company.go:118 pkg/contacts.go:254
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Teléfono"
|
msgstr "Teléfono"
|
||||||
|
|
||||||
#: pkg/company.go:136 pkg/contacts.go:265
|
#: pkg/company.go:136 pkg/contacts.go:270
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Web"
|
msgid "Web"
|
||||||
msgstr "Web"
|
msgstr "Web"
|
||||||
|
|
||||||
#: pkg/company.go:144 pkg/contacts.go:277
|
#: pkg/company.go:144 pkg/contacts.go:282
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Business name"
|
msgid "Business name"
|
||||||
msgstr "Nombre y apellidos"
|
msgstr "Nombre y apellidos"
|
||||||
|
|
||||||
#: pkg/company.go:154 pkg/contacts.go:287
|
#: pkg/company.go:154 pkg/contacts.go:292
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "VAT number"
|
msgid "VAT number"
|
||||||
msgstr "DNI / NIF"
|
msgstr "DNI / NIF"
|
||||||
|
|
||||||
#: pkg/company.go:160 pkg/contacts.go:293
|
#: pkg/company.go:160 pkg/contacts.go:298
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Dirección"
|
msgstr "Dirección"
|
||||||
|
|
||||||
#: pkg/company.go:169 pkg/contacts.go:302
|
#: pkg/company.go:169 pkg/contacts.go:307
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "City"
|
msgid "City"
|
||||||
msgstr "Población"
|
msgstr "Población"
|
||||||
|
|
||||||
#: pkg/company.go:175 pkg/contacts.go:308
|
#: pkg/company.go:175 pkg/contacts.go:313
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Province"
|
msgid "Province"
|
||||||
msgstr "Provincia"
|
msgstr "Provincia"
|
||||||
|
|
||||||
#: pkg/company.go:181 pkg/contacts.go:314
|
#: pkg/company.go:181 pkg/contacts.go:319
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Postal code"
|
msgid "Postal code"
|
||||||
msgstr "Código postal"
|
msgstr "Código postal"
|
||||||
|
|
||||||
#: pkg/company.go:190 pkg/contacts.go:323
|
#: pkg/company.go:190 pkg/contacts.go:328
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Country"
|
msgid "Country"
|
||||||
msgstr "País"
|
msgstr "País"
|
||||||
|
@ -834,23 +846,23 @@ msgctxt "input"
|
||||||
msgid "Legal disclaimer"
|
msgid "Legal disclaimer"
|
||||||
msgstr "Nota legal"
|
msgstr "Nota legal"
|
||||||
|
|
||||||
#: pkg/company.go:271 pkg/contacts.go:375
|
#: pkg/company.go:271 pkg/contacts.go:380
|
||||||
msgid "Selected country is not valid."
|
msgid "Selected country is not valid."
|
||||||
msgstr "Habéis escogido un país que no es válido."
|
msgstr "Habéis escogido un país que no es válido."
|
||||||
|
|
||||||
#: pkg/company.go:275 pkg/contacts.go:378
|
#: pkg/company.go:275 pkg/contacts.go:383
|
||||||
msgid "Business name can not be empty."
|
msgid "Business name can not be empty."
|
||||||
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:276 pkg/contacts.go:379
|
#: pkg/company.go:276 pkg/contacts.go:384
|
||||||
msgid "Business name must have at least two letters."
|
msgid "Business name must have at least two letters."
|
||||||
msgstr "El nombre y los apellidos deben contener como mínimo dos letras."
|
msgstr "El nombre y los apellidos deben contener como mínimo dos letras."
|
||||||
|
|
||||||
#: pkg/company.go:277 pkg/contacts.go:380
|
#: pkg/company.go:277 pkg/contacts.go:385
|
||||||
msgid "VAT number can not be empty."
|
msgid "VAT number can not be empty."
|
||||||
msgstr "No podéis dejar el DNI o NIF en blanco."
|
msgstr "No podéis dejar el DNI o NIF en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:278 pkg/contacts.go:381
|
#: pkg/company.go:278 pkg/contacts.go:386
|
||||||
msgid "This value is not a valid VAT number."
|
msgid "This value is not a valid VAT number."
|
||||||
msgstr "Este valor no es un DNI o NIF válido."
|
msgstr "Este valor no es un DNI o NIF válido."
|
||||||
|
|
||||||
|
@ -858,31 +870,31 @@ msgstr "Este valor no es un DNI o NIF válido."
|
||||||
msgid "Phone can not be empty."
|
msgid "Phone can not be empty."
|
||||||
msgstr "No podéis dejar el teléfono en blanco."
|
msgstr "No podéis dejar el teléfono en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:281 pkg/contacts.go:396
|
#: pkg/company.go:281 pkg/contacts.go:401
|
||||||
msgid "This value is not a valid phone number."
|
msgid "This value is not a valid phone number."
|
||||||
msgstr "Este valor no es un teléfono válido."
|
msgstr "Este valor no es un teléfono válido."
|
||||||
|
|
||||||
#: pkg/company.go:287 pkg/contacts.go:402
|
#: pkg/company.go:287 pkg/contacts.go:407
|
||||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||||
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
|
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
|
||||||
|
|
||||||
#: pkg/company.go:289 pkg/contacts.go:383
|
#: pkg/company.go:289 pkg/contacts.go:388
|
||||||
msgid "Address can not be empty."
|
msgid "Address can not be empty."
|
||||||
msgstr "No podéis dejar la dirección en blanco."
|
msgstr "No podéis dejar la dirección en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:290 pkg/contacts.go:384
|
#: pkg/company.go:290 pkg/contacts.go:389
|
||||||
msgid "City can not be empty."
|
msgid "City can not be empty."
|
||||||
msgstr "No podéis dejar la población en blanco."
|
msgstr "No podéis dejar la población en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:291 pkg/contacts.go:385
|
#: pkg/company.go:291 pkg/contacts.go:390
|
||||||
msgid "Province can not be empty."
|
msgid "Province can not be empty."
|
||||||
msgstr "No podéis dejar la provincia en blanco."
|
msgstr "No podéis dejar la provincia en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:292 pkg/contacts.go:387
|
#: pkg/company.go:292 pkg/contacts.go:392
|
||||||
msgid "Postal code can not be empty."
|
msgid "Postal code can not be empty."
|
||||||
msgstr "No podéis dejar el código postal en blanco."
|
msgstr "No podéis dejar el código postal en blanco."
|
||||||
|
|
||||||
#: pkg/company.go:293 pkg/contacts.go:388
|
#: pkg/company.go:293 pkg/contacts.go:393
|
||||||
msgid "This value is not a valid postal code."
|
msgid "This value is not a valid postal code."
|
||||||
msgstr "Este valor no es un código postal válido válido."
|
msgstr "Este valor no es un código postal válido válido."
|
||||||
|
|
||||||
|
@ -1248,33 +1260,38 @@ msgstr "DD/MM/YYYY"
|
||||||
msgid "Invoice product ID must be a number greater than zero."
|
msgid "Invoice product ID must be a number greater than zero."
|
||||||
msgstr "El ID de producto de factura tiene que ser un número mayor a cero."
|
msgstr "El ID de producto de factura tiene que ser un número mayor a cero."
|
||||||
|
|
||||||
#: pkg/contacts.go:273
|
#: pkg/contacts.go:278
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Need to input tax details"
|
msgid "Need to input tax details"
|
||||||
msgstr "Necesito facturar este contacto"
|
msgstr "Necesito facturar este contacto"
|
||||||
|
|
||||||
#: pkg/contacts.go:333
|
#: pkg/contacts.go:338
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "IBAN"
|
msgid "IBAN"
|
||||||
msgstr "IBAN"
|
msgstr "IBAN"
|
||||||
|
|
||||||
#: pkg/contacts.go:338
|
#: pkg/contacts.go:343
|
||||||
msgctxt "bic"
|
msgctxt "bic"
|
||||||
msgid "BIC"
|
msgid "BIC"
|
||||||
msgstr "BIC"
|
msgstr "BIC"
|
||||||
|
|
||||||
#: pkg/contacts.go:393
|
#: pkg/contacts.go:398
|
||||||
msgid "Name must have at least two letters."
|
msgid "Name must have at least two letters."
|
||||||
msgstr "El nombre debe contener como mínimo dos letras."
|
msgstr "El nombre debe contener como mínimo dos letras."
|
||||||
|
|
||||||
#: pkg/contacts.go:405
|
#: pkg/contacts.go:410
|
||||||
msgid "This values is not a valid IBAN."
|
msgid "This values is not a valid IBAN."
|
||||||
msgstr "Este valor no es un IBAN válido."
|
msgstr "Este valor no es un IBAN válido."
|
||||||
|
|
||||||
#: pkg/contacts.go:408
|
#: pkg/contacts.go:413
|
||||||
msgid "This values is not a valid BIC."
|
msgid "This values is not a valid BIC."
|
||||||
msgstr "Este valor no es un BIC válido."
|
msgstr "Este valor no es un BIC válido."
|
||||||
|
|
||||||
|
#: pkg/contacts.go:527
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Holded Excel file"
|
||||||
|
msgstr "Archivo Excel de Holded"
|
||||||
|
|
||||||
#~ msgctxt "action"
|
#~ msgctxt "action"
|
||||||
#~ msgid "Update contact"
|
#~ msgid "Update contact"
|
||||||
#~ msgstr "Actualizar contacto"
|
#~ msgstr "Actualizar contacto"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Revert numerus:import_contact from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists numerus.end_import_contacts(integer);
|
||||||
|
drop function if exists numerus.begin_import_contacts();
|
||||||
|
|
||||||
|
commit;
|
|
@ -111,3 +111,4 @@ bic [schema_numerus] 2023-07-01T22:46:30Z jordi fita mas <jordi@tandem.blog> # A
|
||||||
contact_swift [schema_numerus roles contact bic] 2023-07-01T23:03:13Z jordi fita mas <jordi@tandem.blog> # Add relation for contact’s SWIFT-BIC
|
contact_swift [schema_numerus roles contact bic] 2023-07-01T23:03:13Z jordi fita mas <jordi@tandem.blog> # Add relation for contact’s SWIFT-BIC
|
||||||
add_contact [add_contact@v0 tax_details contact_web contact_email contact_phone contact_iban contact_swift] 2023-06-29T11:10:15Z jordi fita mas <jordi@tandem.blog> # Change add contact to accept a tax_detail parameter and use the new relations for web, email, phone, iban, and swift
|
add_contact [add_contact@v0 tax_details contact_web contact_email contact_phone contact_iban contact_swift] 2023-06-29T11:10:15Z jordi fita mas <jordi@tandem.blog> # Change add contact to accept a tax_detail parameter and use the new relations for web, email, phone, iban, and swift
|
||||||
edit_contact [edit_contact@v0 tax_details contact_web contact_email contact_phone contact_iban contact_swift] 2023-06-29T11:50:41Z jordi fita mas <jordi@tandem.blog> # Change edit_contact to require tax_details parameter and to use new relations for web, email, phone, iban, and swift
|
edit_contact [edit_contact@v0 tax_details contact_web contact_email contact_phone contact_iban contact_swift] 2023-06-29T11:50:41Z jordi fita mas <jordi@tandem.blog> # Change edit_contact to require tax_details parameter and to use new relations for web, email, phone, iban, and swift
|
||||||
|
import_contact [schema_numerus roles contact contact_web contact_phone contact_email contact_iban contact_swift contact_tax_details] 2023-07-02T18:50:22Z jordi fita mas <jordi@tandem.blog> # Add functions to massively import customer data
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
-- Test import_contact
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select plan(27);
|
||||||
|
|
||||||
|
set search_path to numerus, auth, public;
|
||||||
|
|
||||||
|
select has_function('numerus', 'begin_import_contacts', array []::name[]);
|
||||||
|
select function_lang_is('numerus', 'begin_import_contacts', array []::name[], 'sql');
|
||||||
|
select function_returns('numerus', 'begin_import_contacts', array []::name[], 'name');
|
||||||
|
select isnt_definer('numerus', 'begin_import_contacts', array []::name[]);
|
||||||
|
select volatility_is('numerus', 'begin_import_contacts', array []::name[], 'volatile');
|
||||||
|
select function_privs_are('numerus', 'begin_import_contacts', array []::name[], 'guest', array []::text[]);
|
||||||
|
select function_privs_are('numerus', 'begin_import_contacts', array []::name[], 'invoicer', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'begin_import_contacts', array []::name[], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'begin_import_contacts', array []::name[], 'authenticator', array []::text[]);
|
||||||
|
|
||||||
|
select has_function('numerus', 'end_import_contacts', array ['integer']);
|
||||||
|
select function_lang_is('numerus', 'end_import_contacts', array ['integer'], 'plpgsql');
|
||||||
|
select function_returns('numerus', 'end_import_contacts', array ['integer'], 'integer');
|
||||||
|
select isnt_definer('numerus', 'end_import_contacts', array ['integer']);
|
||||||
|
select volatility_is('numerus', 'end_import_contacts', array ['integer'], 'volatile');
|
||||||
|
select function_privs_are('numerus', 'end_import_contacts', array ['integer'], 'guest', array []::text[]);
|
||||||
|
select function_privs_are('numerus', 'end_import_contacts', array ['integer'], 'invoicer', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'end_import_contacts', array ['integer'], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'end_import_contacts', array ['integer'], 'authenticator', array []::text[]);
|
||||||
|
|
||||||
|
|
||||||
|
set client_min_messages to warning;
|
||||||
|
truncate contact_swift cascade;
|
||||||
|
truncate contact_iban cascade;
|
||||||
|
truncate contact_web cascade;
|
||||||
|
truncate contact_email cascade;
|
||||||
|
truncate contact_phone cascade;
|
||||||
|
truncate contact_tax_details cascade;
|
||||||
|
truncate contact cascade;
|
||||||
|
truncate payment_method cascade;
|
||||||
|
truncate company cascade;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||||
|
|
||||||
|
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||||
|
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||||
|
values (111, 1, 'cash', 'cash')
|
||||||
|
, (112, 1, 'bank', 'send money to my bank account')
|
||||||
|
;
|
||||||
|
|
||||||
|
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||||
|
|
||||||
|
insert into contact (contact_id, company_id, slug, name, tags)
|
||||||
|
values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', '{tag1}')
|
||||||
|
, (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', '{tag2}')
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||||
|
values (12, 'Contact 1 Ltd', 'ES41414141L', 'One Road', 'One City', 'One Province', '17001', 'ES')
|
||||||
|
, (13, 'Contact 2 Ltd', 'ES42424242Y', 'Two Road', 'Two City', 'Two Province', '17002', 'ES')
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_phone (contact_id, phone)
|
||||||
|
values (12, '777-777-777')
|
||||||
|
, (13, '888-888-888')
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_email (contact_id, email)
|
||||||
|
values (12, 'c@c')
|
||||||
|
, (13, 'd@d')
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_web (contact_id, uri)
|
||||||
|
values (12, 'https://1/')
|
||||||
|
, (13, 'https://2/')
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_iban (contact_id, iban)
|
||||||
|
values (12, 'NL04RABO9373475770')
|
||||||
|
, (13, 'NL17RABO4416709382')
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into contact_swift (contact_id, bic)
|
||||||
|
values (12, 'ABNANL2A')
|
||||||
|
, (13, 'ARBNNL22')
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
select is( begin_import_contacts(), 'imported_contact', 'Should return the name of the relation to import' );
|
||||||
|
|
||||||
|
insert into imported_contact (name, vatin, email, phone, web, address, city, province, postal_code, country_code, iban, bic, tags)
|
||||||
|
values ('Contact 1 S.L.', '41414141L', 'a@a', '111-111-111', 'https://a/', 'Fake St., 123', 'Fake City', 'Fake province', '17000', 'ES', 'NL73INGB9691012820', 'EMCFNLKEX30', '#updated') -- valid updated contact
|
||||||
|
, ('Contact 2 Ltd', '42424242Y', 'd@d', '888-888-888', 'https://2/', '', '', '', '', 'ES', 'NL17RABO4416709382', 'ARBNNL22', '') -- valid existing contact, with same data but missing taxt details; leave what we already had
|
||||||
|
, ('Contact 3', '43434343Q', 'e@e', '999-999-999', 'invalid uri', 'Three Road', 'Three City', 'Three Province', '17003', 'FR', 'NL77INGB8674905641', 'EMCFNLKEXXX', '#new') -- valid new contact
|
||||||
|
, ('Contact 4.1', '44444444B', 'invalid email', '000-000-000', '', 'Four Road', 'Four City', 'Four Province', '17004', 'ES', 'invalid iban', 'EMCFNLKEX20', '#missing #details #vatin') -- invalid vatin: no tax details added
|
||||||
|
, ('Contact 4.2', '44444444A', 'f@f', 'invalid phone', '', '', 'Four City', 'Four Province', '17004', 'ES', 'NL50RABO9661117578', 'invalid bic', '#missing #details #street') -- invalid street: no tax details added
|
||||||
|
, ('Contact 4.3', '44444444A', '', '', 'https://4/', 'Four Road', '', 'Four Province', '17004', 'ES', '', '' , '#missing #details #city #$$$$') -- invalid city: no tax details added
|
||||||
|
, ('Contact 4.4', '44444444A', '', '', '', 'Four Road', 'Four City', '', '17004', 'ES', '', '' , '#missing #details #Pro$vince') -- invalid province: no tax details added
|
||||||
|
, ('Contact 4.5', '44444444A', '', '', '', 'Four Road', 'Four City', 'Four Province', '', 'ES', '', '' , '#missing #det/ails #postal code') -- invalid postal code: no tax details added
|
||||||
|
, ('Contact 4.6', '44444444A', '', '', '', 'Four Road', 'Four City', 'Four Province', '17004', '', '', '' , '#mis-sing #details #country') -- invalid country code: no tax details added
|
||||||
|
, ('Contact 5', '', '', '', '', '', '', '', '', '', '', '', 'just name') -- valid new contact with just a name
|
||||||
|
, (' ', '44444444A', 'valid@email.com', '111 111 111', 'https://3/', 'Fake St., 123', 'City', 'Province', '17486', 'ES', 'NL04RABO9373475770', 'ARBNNL22', 'tag1 tag2') -- contact with invalid name — not added
|
||||||
|
;
|
||||||
|
|
||||||
|
select is( end_import_contacts(1), 10, 'Should have imported all contacts with mostly correct data' );
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select company_id, name, tags from contact $$,
|
||||||
|
$$ values (1, 'Contact 1', '{tag1,updated}'::tag_name[])
|
||||||
|
, (1, 'Contact 2', '{tag2}'::tag_name[])
|
||||||
|
, (1, 'Contact 3', '{new}'::tag_name[])
|
||||||
|
, (1, 'Contact 4.1', '{missing,details,vatin}'::tag_name[])
|
||||||
|
, (1, 'Contact 4.2', '{missing,details,street}'::tag_name[])
|
||||||
|
, (1, 'Contact 4.3', '{missing,details,city}'::tag_name[])
|
||||||
|
, (1, 'Contact 4.4', '{missing,details,province}'::tag_name[])
|
||||||
|
, (1, 'Contact 4.5', '{missing,details,postal,code}'::tag_name[])
|
||||||
|
, (1, 'Contact 4.6', '{mis-sing,details,country}'::tag_name[])
|
||||||
|
, (1, 'Contact 5', '{just,name}'::tag_name[])
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts'
|
||||||
|
);
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select name, business_name, vatin::text, address, city, province, postal_code, country_code::text from contact join contact_tax_details using (contact_id) $$,
|
||||||
|
$$ values ('Contact 1', 'Contact 1 S.L.', 'ES41414141L', 'Fake St., 123', 'Fake City', 'Fake province', '17000', 'ES')
|
||||||
|
, ('Contact 2', 'Contact 2 Ltd', 'ES42424242Y', 'Two Road', 'Two City', 'Two Province', '17002', 'ES')
|
||||||
|
, ('Contact 3', 'Contact 3', 'FR43434343Q', 'Three Road', 'Three City', 'Three Province', '17003', 'FR')
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts’ tax details'
|
||||||
|
);
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select name, phone::text from contact join contact_phone using (contact_id) $$,
|
||||||
|
$$ values ('Contact 1', '+34 111111111')
|
||||||
|
, ('Contact 2', '+34 888 88 88 88')
|
||||||
|
, ('Contact 3', '+33 9 99 99 99 99')
|
||||||
|
, ('Contact 4.1', '+34 000000000')
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts’ phone'
|
||||||
|
);
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select name, email::text from contact join contact_email using (contact_id) $$,
|
||||||
|
$$ values ('Contact 1', 'a@a')
|
||||||
|
, ('Contact 2', 'd@d')
|
||||||
|
, ('Contact 3', 'e@e')
|
||||||
|
, ('Contact 4.2', 'f@f')
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts’ email'
|
||||||
|
);
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select name, uri::text from contact join contact_web using (contact_id) $$,
|
||||||
|
$$ values ('Contact 1', 'https://a/')
|
||||||
|
, ('Contact 2', 'https://2/')
|
||||||
|
, ('Contact 4.3', 'https://4/')
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts’ web'
|
||||||
|
);
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select name, iban::text from contact join contact_iban using (contact_id) $$,
|
||||||
|
$$ values ('Contact 1', 'NL73INGB9691012820')
|
||||||
|
, ('Contact 2', 'NL17RABO4416709382')
|
||||||
|
, ('Contact 3', 'NL77INGB8674905641')
|
||||||
|
, ('Contact 4.2', 'NL50RABO9661117578')
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts’ IBAN'
|
||||||
|
);
|
||||||
|
|
||||||
|
select bag_eq(
|
||||||
|
$$ select name, bic::text from contact join contact_swift using (contact_id) $$,
|
||||||
|
$$ values ('Contact 1', 'EMCFNLKEX30')
|
||||||
|
, ('Contact 2', 'ARBNNL22')
|
||||||
|
, ('Contact 3', 'EMCFNLKEXXX')
|
||||||
|
, ('Contact 4.1', 'EMCFNLKEX20')
|
||||||
|
$$,
|
||||||
|
'Should have created all contacts’ BIC'
|
||||||
|
);
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Verify numerus:import_contact on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('numerus.begin_import_contacts()', 'execute');
|
||||||
|
select has_function_privilege('numerus.end_import_contacts(integer)', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -14,12 +14,12 @@
|
||||||
<h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1>
|
<h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1>
|
||||||
<details id="profile-menu" class="menu">
|
<details id="profile-menu" class="menu">
|
||||||
<summary>
|
<summary>
|
||||||
<i class="ri-eye-close-line ri-3x"></i>
|
<i class="ri-user-6-line ri-3x"></i>
|
||||||
</summary>
|
</summary>
|
||||||
<ul role="menu" class="action-menu" data-hx-push-url="false" data-hx-swap="beforeend">
|
<ul role="menu" class="action-menu" data-hx-push-url="false" data-hx-swap="beforeend">
|
||||||
<li role="presentation">
|
<li role="presentation">
|
||||||
<a role="menuitem" href="{{ companyURI "/profile" }}" data-hx-boost="true">
|
<a role="menuitem" href="{{ companyURI "/profile" }}" data-hx-boost="true">
|
||||||
<i class="ri-account-circle-line"></i>
|
<i class="ri-user-6-line"></i>
|
||||||
{{( pgettext "Account" "menu" )}}
|
{{( pgettext "Account" "menu" )}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
{{ define "title" -}}
|
||||||
|
{{( pgettext "Import Contacts" "title" )}}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "breadcrumbs" -}}
|
||||||
|
<nav>
|
||||||
|
<p data-hx-target="main" data-hx-boost="true">
|
||||||
|
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
|
||||||
|
<a href="{{ companyURI "/contacts"}}">{{( pgettext "Contacts" "title" )}}</a> /
|
||||||
|
<a>{{( pgettext "Import Contacts" "title" )}}</a>
|
||||||
|
</p>
|
||||||
|
</nav>
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactImportForm*/ -}}
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
{{ csrfToken }}
|
||||||
|
|
||||||
|
{{ template "file-field" .File }}
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<button class="primary" type="submit">Import</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{{- end }}
|
|
@ -11,6 +11,8 @@
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ template "filters-toggle" }}
|
{{ template "filters-toggle" }}
|
||||||
|
<a class="button"
|
||||||
|
href="{{ companyURI "/contacts/import" }}">{{( pgettext "Import" "action" )}}</a>
|
||||||
<a class="primary button"
|
<a class="primary button"
|
||||||
href="{{ companyURI "/contacts/new" }}">{{( pgettext "New contact" "action" )}}</a>
|
href="{{ companyURI "/contacts/new" }}">{{( pgettext "New contact" "action" )}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
{{ define "file-field" -}}
|
{{ define "file-field" -}}
|
||||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.FileField*/ -}}
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.FileField*/ -}}
|
||||||
<div class="input{{ if .Errors }} has-errors{{ end }}">
|
<div class="input{{ if .Errors }} has-errors{{ end }}">
|
||||||
<input type="file" name="{{ .Name }}" id="{{ .Name }}-field" placeholder="{{ .Label }}">
|
<input type="file" name="{{ .Name }}" id="{{ .Name }}-field" placeholder="{{ .Label }}"{{ if .Required}} required="required"{{ end }}>
|
||||||
<label for="{{ .Name }}-field">{{ .Label }}{{ if gt .MaxSize 0 }} {{printf (pgettext "(Max. %s)" "input") (.MaxSize|humanizeBytes) }}{{ end }}</label>
|
<label for="{{ .Name }}-field">{{ .Label }}{{ if gt .MaxSize 0 }} {{printf (pgettext "(Max. %s)" "input") (.MaxSize|humanizeBytes) }}{{ end }}</label>
|
||||||
{{- if .Errors }}
|
{{- if .Errors }}
|
||||||
<ul>
|
<ul>
|
||||||
|
|
Loading…
Reference in New Issue