Correction : data/members.import : abstraction intermédiaire pour une meilleure synthèse.
authorJulien Moutinho <julm@autogeree.net>
Mon, 28 Oct 2013 02:05:34 +0000 (03:05 +0100)
committerJulien Moutinho <julm@autogeree.net>
Mon, 28 Oct 2013 02:05:34 +0000 (03:05 +0100)
data/members.import

index 1037b27..ea2fdd0 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 #!/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;
 use strict;
 use warnings FATAL => qw(all);
 use utf8;
@@ -13,150 +14,244 @@ require Text::CSV;
 require XML::Generator;
 require Text::Trim;
 
 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 ($_) = @_;
        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
  }
 
 sub main () {
        my $csv = Text::CSV->new
-        ({ binary => 1, eol => $/
+        ({binary => 1
+        , eol => $/
         , sep_char => ';'
         });
        my $xml = XML::Generator->new
         , sep_char => ';'
         });
        my $xml = XML::Generator->new
@@ -166,39 +261,60 @@ sub main () {
         , pretty      => 2
         );
        my $in = IO::Wrap::wraphandle(\*STDIN);
         , pretty      => 2
         );
        my $in = IO::Wrap::wraphandle(\*STDIN);
+       
        my $csv_head = $csv->getline($in);
        #print STDERR ("head: ", join("|", @$csv_head), "\n");
        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"
                 }
         ( { 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
         );
        binmode STDOUT, ':utf8';
        print $xml->openerp
-        ( $xml->data(@$data)
+        ( $xml->data(@$xml_data)
         , $xml->data
         , $xml->data
-                ( {noupdate=>"1"}
-                , @$data_noupdate )
+                ( {noupdate => "1"}
+                , @$xml_data_noupdate )
         );
        
        my $out = IO::Wrap::wraphandle(\*STDERR);
         );
        
        my $out = IO::Wrap::wraphandle(\*STDERR);
-       foreach (@$data_rejected) {
-               $csv->print($out, $_);
-        }
+       print STDERR "csv_lines_rejected=".Data::Dumper::Dumper($csv_lines_rejected);
  }
 
 main;
  }
 
 main;