From: Julien Moutinho Date: Mon, 28 Oct 2013 02:05:34 +0000 (+0100) Subject: Correction : data/members.import : abstraction intermédiaire pour une meilleure synthèse. X-Git-Url: http://git.cyclocoop.org/?p=burette%2Fcyclofficine_ivry.git;a=commitdiff_plain;h=de08f08a46e53f6236ae5ceaff3c373b4b085b59 Correction : data/members.import : abstraction intermédiaire pour une meilleure synthèse. --- diff --git a/data/members.import b/data/members.import index 1037b27..ea2fdd0 100755 --- a/data/members.import +++ b/data/members.import @@ -1,6 +1,7 @@ #!/usr/bin/perl +our $partner = "cyclofficine_ivry"; -our $VERSION = '0.0.1'; +our $VERSION = '2013.10.27'; use strict; use warnings FATAL => qw(all); use utf8; @@ -13,150 +14,244 @@ require Text::CSV; require XML::Generator; require Text::Trim; -sub parse_date ($) { - my ($_) = @_; - if ($_) { - 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"; - } - else { return undef }; +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_euro ($) { +sub parse_amount (@) { my ($_) = @_; - if ($_) { - my ($num) = ($_ =~ m{^\s*([0-9]+),00\s*€?.$}); - return $num; + 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 $_; } - else { return undef }; } -sub parse_line (@) { - my ($pass, $xml, $number_next, $adh, $data, $data_noupdate, $data_rejected, $data_reparse) = @_; - my ($number, $date_cot1, $cot1, $moyen_cot1, $reduction, $genre, $prenom, $nom, $naissance - , $email, $tel_fixe, $tel_mobile, $addr_postale, $cp, $city, $comment) = - #map {Text::Trim::trim($_)} - @$adh; - my $partner = "cyclofficine_ivry"; - $number = Text::Trim::trim($number); - if (not ($number =~ m/^[0-9]+$/)) { - if ($pass == 0) { - push @$data_reparse, $adh; - return ($number_next); - } - else { - $comment - =($comment?"$comment. ":"") - ."(n° malformé d'origine : $number)"; - $number = "$number_next"; - } +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; } - if ($number =~ m/^[0-9]+$/) { - if ($nom or $email) { - $number = $number + 0; - $number_next = $number + 1; - $date_cot1 = parse_date($date_cot1); - #$date_cot2 = parse_date($date_cot2); - $cot1 = parse_euro($cot1); - #$cot2 = parse_euro($cot2); - my $city = ($cp and ($cp =~ m/^750[0-2][0-9]$/) ? "Paris" : undef); - my $country = ($cp and ($cp =~ m/^UK$/) ? "Royaume-Uni" : "France"); - my $phone = undef; - my $street = undef; - my %reductions = - ( "Chômeur" => "unemployed" - , "Chmeur" => "unemployed" - , "Atelier vélo IdF" => "velorution_idf" - , "Étudiant" => "student" - , "Etudiant" => "student" - , "etudiant" => "student" - , "Retraité" => "retired" - ); - $reduction = Text::Trim::trim($reduction); - $reduction - = exists $reductions{$reduction} - ? $reductions{$reduction} - : "standard"; - my %pay_accounts = - ( "Espèces" => "cash" - , "Chèque" => "bank" - ); - my $pay_account_cot1 = Text::Trim::trim($moyen_cot1); - $pay_account_cot1 - = exists $pay_accounts{$pay_account_cot1} - ? $pay_accounts{$pay_account_cot1} - : "cash"; - push @$data, $xml->record - ( { id => "res_partner_${partner}_$number" - , model => "res.partner" + 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}; } - , $xml->field({name => "name"}, $nom . ($prenom ? " $prenom" : "")) - , $xml->field({name => "member_ident"}, $number) - , $xml->field({name => "type"}, "default") - , ($cp ? $xml->field({name => "zip"}, $cp) : ()) - , ($city ? $xml->field({name => "city"}, $city) : ()) - , ($country ? $xml->field({name => "country_id", model => "res.country", search => "[('name','=','$country')]"}) : ()) - , ($email ? $xml->field({name => "email"}, $email) : ()) - , ($phone ? $xml->field({name => "phone"}) : ()) - , ($addr_postale? $xml->field({name => "street"}, $addr_postale) : ()) - , ($comment ? $xml->field({name => "comment"}, $comment) : ()) - ); - my %cots = - ( ($cot1 ? ($cot1 => $date_cot1) : ()) - #, ($cot2 ? ($cot2 => $date_cot2) : ()) - ) ; - while ( my ($amount, $date_from) = each(%cots) ) { - push @$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}_$number')"}) - , $xml->xmlcmnt('product_id') - , $xml->value({eval => "ref('product_${reduction}_member')"}) - , $xml->xmlcmnt('context') - , $xml->value({eval => "{'amount':$amount, 'date_from':'$date_from'}"}) - ) - ) - , $xml->xmlcmnt('pay_amount') - , $xml->value ({eval => "$amount"}) - , $xml->xmlcmnt('pay_account_id') - , $xml->value ({model => "account.account", search => "[('name', '=', 'Cash')]"}) - , $xml->xmlcmnt("moyen_cot1: $moyen_cot1") - , $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')"}) - ); + 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; } } - else { - push @$data_rejected, $adh; + } + } +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')"}) + ); } - return ($number_next); } sub main () { my $csv = Text::CSV->new - ({ binary => 1, eol => $/ + ({binary => 1 + , eol => $/ , sep_char => ';' }); my $xml = XML::Generator->new @@ -166,39 +261,60 @@ sub main () { , 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); - my $data = []; - my $data_noupdate = []; - my $data_rejected = []; - my $data_reparse = []; - my $number_next = 1; - while (my $adh = $csv->getline($in)) { - #print STDERR ("line: ", join("|", @$adh), "\n"); - ($number_next) = parse_line(0, $xml, $number_next, $adh, $data, $data_noupdate, $data_rejected, $data_reparse); + #$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 (@$data_reparse) { - ($number_next) = parse_line(1, $xml, $number_next, $_, $data, $data_noupdate, $data_rejected, []); + foreach my $number (sort {$a <=> $b} (keys %$members)) { + xml_of_member($xml, $members->{$number}, $xml_data, $xml_data_noupdate); } - push @$data_noupdate, $xml->record + push @$xml_data_noupdate, $xml->record ( { id => "remembership.member_ident_sequence" , model => "ir.sequence" } - , $xml->field({name => "number_next"}, $number_next) + , $xml->field({name => "number_next"}, $greatest_number + 1) ); binmode STDOUT, ':utf8'; print $xml->openerp - ( $xml->data(@$data) + ( $xml->data(@$xml_data) , $xml->data - ( {noupdate=>"1"} - , @$data_noupdate ) + ( {noupdate => "1"} + , @$xml_data_noupdate ) ); my $out = IO::Wrap::wraphandle(\*STDERR); - foreach (@$data_rejected) { - $csv->print($out, $_); - } + print STDERR "csv_lines_rejected=".Data::Dumper::Dumper($csv_lines_rejected); } main;