-#!/usr/bin/perl
-our $partner = "cyclofficine_ivry";
-
-our $VERSION = '2013.10.27';
-use strict;
-use warnings FATAL => qw(all);
-use utf8;
-use open qw/:std :utf8/;
-require Data::Dumper;
-require Encode;
-require IO::Wrap;
-require Text::CSV;
-#require Text::CSV::Encoded;
-require XML::Generator;
-require Text::Trim;
-
-sub parse_date (@) {
- ($_) = @_;
- my ($jj,$mm,undef,$yy) = ($_ =~ m{^\s*([0-3]?[0-9])\s*/\s*([0-1]?[0-9])\s*/\s*(20)?([0-9][0-9])\s*$});
- return "20$yy-$mm-$jj";
- }
-sub parse_amount (@) {
- my ($_) = @_;
- my %amounts =
- ( "gratuit" => 0
- , "Gratuit" => 0
- , "offert" => 0
- , "Offert" => 0
- , "?" => 0
- );
- $_ = Text::Trim::trim($_);
- if (exists $amounts{$_}) {
- return $amounts{$_};
- }
- else {
- ($_) = ($_ =~ m{^\s*([0-9]+),00\s*€?.$});
- return $_;
- }
- }
-sub parse_payment_mean (@) {
- ($_) = @_;
- my %payment_means =
- ( "Espèces" => "cash"
- , "Offert" => "cash"
- , "Chèque" => "bank"
- );
- $_ = Text::Trim::trim($_);
- return exists $payment_means{$_}
- ? $payment_means{$_}
- : "cash";
- }
-sub parse_discount ($) {
- ($_) = @_;
- my %discounts =
- ( "Chômeur" => "unemployed"
- , "Chmeur" => "unemployed"
- , "Atelier vélo IdF" => "velorution_idf"
- , "Étudiant" => "student"
- , "Etudiant" => "student"
- , "etudiant" => "student"
- , "Retraité" => "retired"
- );
- $_ = Text::Trim::trim($_);
- return exists $discounts{$_}
- ? $discounts{$_}
- : "standard";
- }
-sub parse_gender (@) {
- ($_) = @_;
- my %genders =
- ( "Ass." => "association"
- , "M." => "male"
- , "Mme" => "female"
- , "Mme." => "female"
- );
- $_ = Text::Trim::trim($_);
- return exists $genders{$_}
- ? $genders{$_}
- : undef;
- }
-
-our $last_number = -1;
-our $greatest_number = -1;
-sub member_of_csv_line (@) {
- my ($members, $csv_line, $csv_lines_rejected, $csv_lines_to_reparse) = @_;
- print STDERR ("member_of_csv_line: csv_line=".Data::Dumper::Dumper($csv_line));
- my $number = Text::Trim::trim($csv_line->{number});
- if (not $number) {
- push @$csv_lines_rejected, $csv_line;
- }
- else {
- my $member = {};
- if (not $number or not ($number =~ m/^[0-9]+$/)) {
- if (defined $csv_lines_to_reparse) {
- push @$csv_lines_to_reparse, $csv_line;
- return;
- }
- else {
- $greatest_number = $greatest_number + 1;
- print STDERR "WARNING: renumérotation: ".($number?$number:"undef")." -> $greatest_number\n";
- $csv_line->{comment}
- =($csv_line->{comment}?"$csv_line->{comment}. ":"")
- ."(n° malformé d'origine : ".($number?$number:"undef").")";
- $number = "$greatest_number";
- }
- }
- if ($number =~ m/^[0-9]+$/) {
- $number = $number + 0;
- if ($last_number + 1 != $number + 0) {
- print STDERR "WARNING: discontinuité: attendu=".($last_number + 1)." eu=".($number + 0)."\n";
- }
- $last_number = $number;
- $greatest_number = $number
- if $number > $greatest_number;
-
- if ($csv_line->{name} or $csv_line->{email}) {
- if (exists $members->{$number}) {
- $member = $members->{$number};
- }
- else {
- $member = {};
- $members->{$number} = $member;
- }
- $member->{number} = $number;
- $member->{name}
- = $csv_line->{name}
- unless $member->{name};
- $member->{firstname}
- = $csv_line->{firstname}
- unless $member->{firstname};
- $member->{email}
- = $csv_line->{email}
- unless $member->{email};
- $member->{cotisations}
- = []
- unless exists $member->{cotisations};
- push @{$member->{cotisations}},
- { amount => parse_amount($csv_line->{cotisation_amount})
- , date => parse_date($csv_line->{cotisation_date})
- , discount => parse_discount($csv_line->{cotisation_discount})
- , mean => parse_payment_mean($csv_line->{cotisation_mean})
- };
- $member->{zip}
- =($csv_line->{zip}
- ? $csv_line->{zip}
- : undef)
- unless $member->{zip};
- $member->{gender}
- =($csv_line->{gender}
- ? parse_gender($csv_line->{gender})
- : undef)
- unless $member->{gender};
- $member->{city}
- =($csv_line->{city}
- ? $csv_line->{city}
- : ($member->{zip} and ($member->{zip} =~ m/^750[0-2][0-9]$/) ? "Paris" : undef))
- unless $member->{city};
- $member->{country}
- = ($member->{zip} and ($member->{zip} =~ m/^UK$/)
- ? "Royaume-Uni"
- : "France")
- unless $member->{country};
- $member->{phone}
- =($csv_line->{landline_phone}
- ? $csv_line->{landline_phone}
- :($csv_line->{mobile_phone}
- ? $csv_line->{mobile_phone}
- : undef))
- unless $member->{phone};
- $member->{street}
- =($csv_line->{street}
- ? $csv_line->{street}
- : undef)
- unless $member->{street};
- $member->{comment}
- =($csv_line->{comment}
- ? $csv_line->{comment}
- : undef)
- unless $member->{comment};
- }
- else {
- push @$csv_lines_rejected, $csv_line;
- }
- }
- }
- }
-sub xml_of_member (@) {
- my ($xml, $member, $xml_data, $xml_data_noupdate) = @_;
- print STDERR ("xml_of_member: member=".Data::Dumper::Dumper($member));
- push @$xml_data, $xml->record
- ( { id => "res_partner_${partner}_".$member->{number}
- , model => "res.partner"
- }
- , $xml->field({name => "name"}, $member->{name} . ($member->{firstname} ? " ".$member->{firstname} : ""))
- , $xml->field({name => "member_ident"}, $member->{number})
- , $xml->field({name => "type"}, "default")
- , ($member->{zip} ? $xml->field({name => "zip"}, $member->{zip}) : ())
- , ($member->{city} ? $xml->field({name => "city"}, $member->{city}) : ())
- , ($member->{country}? $xml->field({name => "country_id", model => "res.country", search => "[('name','=','".$member->{country}."')]"}) : ())
- , ($member->{email} ? $xml->field({name => "email"}, $member->{email}) : ())
- , ($member->{phone} ? $xml->field({name => "phone"}, $member->{phone}) : ())
- , ($member->{street} ? $xml->field({name => "street"}, $member->{street}) : ())
- , ($member->{comment}? $xml->field({name => "comment"}, $member->{comment}) : ())
- );
- die unless $member->{cotisations};
- foreach my $cotisation (@{$member->{cotisations}}) {
- push @$xml_data_noupdate, $xml->function
- ( { model => "account.invoice"
- , name => "pay_and_reconcile"
- }
- , $xml->xmlcmnt('ids')
- , $xml->function
- ( { model => "account.invoice"
- , name => "draft2open"
- }
- , $xml->function
- ( { model => "res.partner"
- , name => "create_membership_invoice"
- }
- , $xml->xmlcmnt('partner_id')
- , $xml->value({eval => "ref('res_partner_${partner}_".$member->{number}."')"})
- , $xml->xmlcmnt('product_id')
- , $xml->value({eval => "ref('product_".$cotisation->{discount}."_member')"})
- , $xml->xmlcmnt('context')
- , $xml->value({eval => "{'amount':".$cotisation->{amount}.", 'date_from':'".$cotisation->{date}."'}"})
- )
- )
- , $xml->xmlcmnt('pay_amount')
- , $xml->value ({eval => "$cotisation->{amount}"})
- , $xml->xmlcmnt('pay_account_id')
- , $xml->value ({model => "account.account", search => "[('name', '=', 'Cash')]"})
- , $xml->xmlcmnt("mean: $cotisation->{mean}")
- , $xml->xmlcmnt('period_id')
- , $xml->value ({model => "account.period", search => "[('name', '=', time.strftime('%m/%Y'))]"})
- , $xml->xmlcmnt('pay_journal_id')
- , $xml->value ({model => "account.journal", search => "[('name', '=', 'Cash')]"})
- , $xml->xmlcmnt('writeoff_acc_id')
- , $xml->value ({model => "account.account", search => "[('name', '=', 'Cash')]"})
- , $xml->xmlcmnt('writeoff_period_id')
- , $xml->value ({model => "account.period", search => "[('name', '=', time.strftime('%m/%Y'))]"})
- , $xml->xmlcmnt('writeoff_journal_id')
- , $xml->value ({model => "account.journal", search => "[('name', '=', 'Cash')]"})
- , $xml->xmlcmnt('context')
- , $xml->value ({eval => "{}"})
- , $xml->xmlcmnt('name')
- , $xml->value ({eval => "str('Import de paiement automatique')"})
- );
- }
- }
-
-sub main () {
- my $csv = Text::CSV->new
- ({binary => 1
- , eol => $/
- , sep_char => ';'
- });
- my $xml = XML::Generator->new
- ( escape => 'always'
- , conformance => 'strict'
- , empty => 'self'
- , pretty => 2
- );
- my $in = IO::Wrap::wraphandle(\*STDIN);
-
- my $csv_head = $csv->getline($in);
- #print STDERR ("head: ", join("|", @$csv_head), "\n");
- #$csv->column_names(@$csv_head);
- $csv->column_names (qw (
- number
- cotisation_date
- cotisation_amount
- cotisation_mean
- cotisation_discount
- gender
- firstname
- name
- birth
- email
- landline_phone
- mobile_phone
- street
- zip
- city
- comment
- ));
- my $xml_data = [];
- my $xml_data_noupdate = [];
- my $csv_lines_rejected = [];
- my $csv_lines_to_reparse = [];
- my $members = {};
- while (my $csv_line = $csv->getline_hr($in)) {
- #print STDERR ("csv_line: ", join("|", @$csv_line), "\n");
- member_of_csv_line($members, $csv_line, $csv_lines_rejected, $csv_lines_to_reparse);
- }
- print STDERR "csv_lines_to_reparse=".Data::Dumper::Dumper($csv_lines_to_reparse);
- foreach my $csv_line (@$csv_lines_to_reparse) {
- member_of_csv_line($members, $csv_line, $csv_lines_rejected, undef);
- }
- foreach my $number (sort {$a <=> $b} (keys %$members)) {
- xml_of_member($xml, $members->{$number}, $xml_data, $xml_data_noupdate);
- }
- push @$xml_data_noupdate, $xml->record
- ( { id => "remembership.member_ident_sequence"
- , model => "ir.sequence"
- }
- , $xml->field({name => "number_next"}, $greatest_number + 1)
- );
- binmode STDOUT, ':utf8';
- print $xml->openerp
- ( $xml->data(@$xml_data)
- , $xml->data
- ( {noupdate => "1"}
- , @$xml_data_noupdate )
- );
-
- my $out = IO::Wrap::wraphandle(\*STDERR);
- print STDERR "csv_lines_rejected=".Data::Dumper::Dumper($csv_lines_rejected);
- }
-
-main;