package Net::KashFlow;
use Carp qw/croak/;
use warnings;
use strict;
use Net::KashFlowAPI;    # Autogenerated SOAP::Lite stubs


our $VERSION = '0.04'; # VERSION: generated by DZP::OurPkgVersion

sub new {
    my ( $self, %args ) = @_;
    for (qw/username password/) {
        croak("You need to pass a '$_'") unless $args{$_};
    }
    bless {%args}, $self;
}

sub _c {
    my ( $self, $method, @args ) = @_;
    my ( $result, $status, $explanation ) =
      KashFlowAPI->$method( $self->{username}, $self->{password}, @args );
    if ($explanation) { croak($explanation) }
    return $result;
}


sub get_supplier {
    my ( $self, $thing, $by_id ) = @_;
    my $method = "GetSupplier";
    if ($by_id) { $method .= "ByID" }
    my $supplier;
    eval { $supplier = $self->_c( $method, $thing ) };
    die $@ . "\n" if $@;
    return 0 unless $supplier->{SupplierID};
    $supplier = bless $supplier, "Net::KashFlow::Supplier";
    $supplier->{kf} = $self;
    return $supplier;
}


sub get_supplier_by_id { $_[0]->get_supplier( $_[1], 1 ) }


sub get_customer {
    my ( $self, $thing, $by_id ) = @_;
    my $method = "GetCustomer";
    if ( $thing =~ /@/ ) { $method .= "ByEmail" }
    if ($by_id)          { $method .= "ByID" }
    my $customer;
    eval { $customer = $self->_c( $method, $thing ) };
    if ( $@ =~ /no customer/ ) { return }
    die $@ . "\n" if $@;
    $customer = bless $customer, "Net::KashFlow::Customer";
    $customer->{kf} = $self;
    return $customer;
}


sub get_customer_by_id { $_[0]->get_customer( $_[1], 1 ) }


sub get_customers {
    my $self = shift;
    return
      map { $_->{kf} = $self; bless $_, "Net::KashFlow::Customer" }
      @{ $self->_c("GetCustomers")->{Customer} };
}


sub create_customer {
    my ( $self, $data ) = @_;
    my $id = $self->_c( "InsertCustomer", $data );
    return $self->get_customer_by_id($id);
}


sub get_invoice {
    my ( $self, $thing, $by_id ) = @_;
    my $method = "GetInvoice";
    if ($by_id) { $method .= "ByID" }
    my $invoice;
    eval { $invoice = $self->_c( $method, $thing ) };
    if ( $@ =~ /no invoice/ ) { return }
    die $@ . "\n" if $@;
    return unless $invoice->{InvoiceNumber};
    $invoice = bless $invoice, "Net::KashFlow::Invoice";
    $invoice->{kf} = $self;
    $invoice->{Lines} = bless $invoice->{Lines}, "InvoiceLineSet";    # Urgh
    return $invoice;
}
sub get_invoice_by_id { $_[0]->get_invoice( $_[1], 1 ) }


sub get_invoice_pdf {
    my ( $self, $id ) = @_;
    my $i = $self->get_invoice($id);
    return unless $i;
    my $uri = undef;
    eval { $uri = $self->_c( "PrintInvoice", $id ) };
    die $@ . "\n" if $@;
    return $uri;
}


sub get_invoice_paypal_link {
    my ( $self, $id ) = @_;
    my $i = $self->get_invoice($id);
    return unless $i;
    my $uri = undef;
    eval { $uri = $self->_c( "GetPaypalLink", $id ) };
    die $@ . "\n" if $@;
    return $uri;
}


sub get_overdue_invoices {
    my ($self) = @_;
    my $invoices = undef;
    eval { $invoices = $self->_c("GetInvoices_Overdue") };
    die $@ . "\n" if $@;
    my @invoices = ();
    if ( ref $invoices->{Invoice} eq 'ARRAY' ) {
        for my $i ( @{ $invoices->{Invoice} } ) {
            $i = bless $i, "Net::KashFlow::Invoice";
            $i->{kf} = $self;
            $i->{Lines} = bless $i->{Lines}, "InvoiceLineSet";
            push @invoices, $i;
        }
    }
    else {
        my $i = $invoices->{Invoice};
        return unless $i->{InvoiceNumber};
        $i = bless $i, "Net::KashFlow::Invoice";
        $i->{kf} = $self;
        $i->{Lines} = bless $i->{Lines}, "InvoiceLineSet";
        push @invoices, $i;
    }
    return @invoices;
}


sub get_unpaid_invoices {
    my ($self) = @_;
    my $invoices = undef;
    eval { $invoices = $self->_c("GetInvoices_Unpaid") };
    die $@ . "\n" if $@;
    my @invoices = ();
    if ( ref $invoices->{Invoice} eq 'ARRAY' ) {
        for my $i ( @{ $invoices->{Invoice} } ) {
            $i = bless $i, "Net::KashFlow::Invoice";
            $i->{kf} = $self;
            $i->{Lines} = bless $i->{Lines}, "InvoiceLineSet";
            push @invoices, $i;
        }
    }
    else {
        my $i = $invoices->{Invoice};
        return unless $i->{InvoiceNumber};
        $i = bless $i, "Net::KashFlow::Invoice";
        $i->{kf} = $self;
        $i->{Lines} = bless $i->{Lines}, "InvoiceLineSet";
        push @invoices, $i;
    }
    return @invoices;
}


sub get_invoices_for_customer {
    my ( $self, $customer ) = @_;
    my $c = $self->get_customer_by_id($customer);
    die "No such customer" unless $c;
    my $invoices = ();
    eval { $invoices = $self->_c( "GetInvoicesForCustomer", $customer ) };
    die $@ . "\n" if $@;
    my @invoices = ();
    if ( ref $invoices->{Invoice} eq 'ARRAY' ) {

        for my $i ( @{ $invoices->{Invoice} } ) {
            $i = bless $i, "Net::KashFlow::Invoice";
            $i->{kf} = $self;
            $i->{Lines} = bless $i->{Lines}, "InvoiceLineSet";
            push @invoices, $i;
        }
    }
    else {
        my $i = $invoices->{Invoice};
        return unless $i->{InvoiceNumber};
        $i = bless $i, "Net::KashFlow::Invoice";
        $i->{kf} = $self;
        $i->{Lines} = bless $i->{Lines}, "InvoiceLineSet";
        push @invoices, $i;
    }
    return @invoices;
}


sub create_invoice {
    my ( $self, $data ) = @_;
    my $id = $self->_c( "InsertInvoice", $data );
    return $self->get_invoice($id);
}


sub delete_invoice {
    my ( $self, $data ) = @_;
    my $invoice = $self->get_invoice($data);
    return 0 if !$invoice;
    eval { $self->_c( "DeleteInvoice", $data ) };
    die $@ . "\n" if $@;
    $invoice = undef;
    $invoice = $self->get_invoice($data);
    return 0 if $invoice->{InvoiceNumber};
    return 1;
}


sub get_receipt {
    my ( $self, $thing, $by_id ) = @_;
    my $method = "GetReceipt";
    if ($by_id) { $method .= "ByID" }
    my $receipt;
    eval { $receipt = $self->_c( $method, $thing ) };
    if ( $@ =~ /no receipt/ ) { return }
    die $@ . "\n" if $@;
    return unless $receipt->{InvoiceNumber};
    $receipt = bless $receipt, "Net::KashFlow::Receipt";
    $receipt->{kf} = $self;
    $receipt->{Lines} = bless $receipt->{Lines}, "InvoiceLineSet";    # Urgh
    return $receipt;
}


sub get_receipt_by_id { $_[0]->get_receipt( $_[1], 1 ) }


sub get_receipts_for_supplier {
    my ( $self, $id ) = @_;
    my $s = $self->get_supplier_by_id($id);
    die "No such supplier" unless $s;
    my $rs = ();
    eval { $rs = $self->_c( "GetReceiptsForSupplier", $s->SupplierID ) };
    die $@ . "\n" if $@;
    my @rs = ();
    if ( ref $rs->{Invoice} eq 'ARRAY' ) {

        for my $r ( @{ $rs->{Invoice} } ) {
            $r = bless $r, "Net::KashFlow::Receipt";
            $r->{kf} = $self;
            $r->{Lines} = bless $r->{Lines}, "InvoiceLineSet";
            push @rs, $r;
        }
    }
    else {
        my $r = $rs->{Invoice};
        return unless $r->{InvoiceNumber};
        $r = bless $r, "Net::KashFlow::Receipt";
        $r->{kf} = $self;
        $r->{Lines} = bless $r->{Lines}, "InvoiceLineSet";
        push @rs, $r;
    }
    return @rs;
}


sub create_receipt {
    my ( $self, $data ) = @_;
    my $id = $self->_c( "InsertReceipt", $data );
    return $self->get_receipt($id);
}


sub get_invoice_payment {
    my ( $self, $data ) = @_;
    my $payment;
    eval { $payment = $self->_c( "GetInvoicePayment", $data ) };
    die $@ . "\n" if $@;
    return unless $payment->{Payment};
    $payment = bless $payment->{Payment}, "Net::KashFlow::Payment";
    $payment->{kf} = $self;
    return $payment;
}


sub delete_invoice_payment {
    my ( $self, $data ) = @_;
    eval { $self->_c( "DeleteInvoicePayment", $data ) };
    die $@ . "\n" if $@;
    return 1;
}


sub get_vat_rates {
    my $self = shift;
    my $rates;
    eval { $rates = $self->_c("GetVATRates") };
    die $@ . "\n" if $@;

    my @rates;

    if ( ref $rates->{BasicDataset} eq 'ARRAY' ) {
        for my $r ( @{ $rates->{BasicDataset} } ) {
            push @rates, $r->{Value};
        }
    }
    else {
        push @rates, $rates->{BasicDataset}->{Value};
    }
    return @rates;
}

package Net::KashFlow::Base;
use base 'Class::Accessor';

sub update {
    my $self = shift;
    my $copy = {%$self};
    delete $copy->{kf};
    $self->{kf}->_c( "Update" . $self->_this(), $copy );
}

sub delete {
    my $self = shift;
    my $copy = {%$self};
    delete $copy->{kf};
    $self->{kf}->_c( "Delete" . $self->_this(), $copy );
}

package Net::KashFlow::Customer;


use base 'Net::KashFlow::Base';
__PACKAGE__->mk_accessors(
    qw(
      CustomerID Code Name Contact Telephone Mobile Fax Email Address1 Address2
      Address3 Address4 Postcode Website EC OutsideEC Notes Source Discount
      ShowDiscount PaymentTerms ExtraText1 ExtraText2 ExtraText3 ExtraText4
      ExtraText5 ExtraText6 ExtraText7 ExtraText8 ExtraText9 ExtraText10 ExtraText11
      ExtraText12 ExtraText13 ExtraText14 ExtraText15 ExtraText16 ExtraText17
      ExtraText18 ExtraText19 ExtraText20 CheckBox1 CheckBox2 CheckBox3 CheckBox4
      CheckBox5 CheckBox6 CheckBox7 CheckBox8 CheckBox9 CheckBox10 CheckBox11
      CheckBox12 CheckBox13 CheckBox14 CheckBox15 CheckBox16 CheckBox17 CheckBox18
      CheckBox19 CheckBox20 Created Updated CurrencyID ContactTitle ContactFirstName
      ContactLastName CustHasDeliveryAddress DeliveryAddress1 DeliveryAddress2
      DeliveryAddress3 DeliveryAddress4 DeliveryPostcode
      )
);
sub _this { "Customer" }

sub invoices {
    my $self = shift;
    return map {
        $_->{kf} = $self->{kf};
        $_->{Lines} = bless $_->{Lines}, "InvoiceLineSet";    # Urgh
        bless $_, "Net::KashFlow::Invoice"
      } @{ $self->{kf}->_c( "GetInvoicesForCustomer", $self->CustomerID )
          ->{Invoice} };
}

package Net::KashFlow::Invoice;
use base 'Net::KashFlow::Base';
sub _this { "Invoice" }
__PACKAGE__->mk_accessors(
    qw/
      DueDate NetAmount ProjectID Lines CustomerReference InvoiceDate InvoiceNumber
      SuppressTotal CustomerID Customer CurrencyCode ReadableString ExchangeRate
      VATAmount AmountPaid Paid InvoiceDBID EstimateCategory NetAmount
      /
);


sub add_line {
    my ( $self, $data ) = @_;
    $self->{kf}->_c( "InsertInvoiceLine", $self->InvoiceDBID, $data );
}

sub pay {
    my ( $self, $data ) = @_;
    $data->{PayInvoice} = $self->{InvoiceNumber};
    $self->{kf}->_c( "InsertInvoicePayment", $data );
}

sub email {
    my ( $self, $data ) = @_;
    $data->{InvoiceNumber} = $self->{InvoiceNumber};
    for (qw/FromEmail FromName SubjectLine Body RecipientEmail/) {
        die "You must supply the $_ parameter" unless $data->{$_};
    }
    $self->{kf}->_c( "EmailInvoice", $data );
}

sub delete {
    my ( $self, $data ) = @_;
    $data->{InvoiceNumber} = $self->{InvoiceNumber};
    $self->{kf}->_c( "DeleteInvoice", $data );
}

package Net::KashFlow::Receipt;
use base 'Net::KashFlow::Base';
sub _this { "Receipt" }
__PACKAGE__->mk_accessors(
    qw/
      InvoiceDBID InvoiceNumber InvoiceDate DueDate Paid CustomerID
      CustomerReference NetAmount VatAmount AmountPaid Lines
      /
);


sub add_line {
    my ( $self, $data ) = @_;
    $self->{kf}->_c( "InsertReceiptLine", $self->InvoiceDBID, $data );
}

sub delete_line {
    my ( $self, $id ) = @_;
    $self->{kf}->_c( "DeleteReceiptLine", $id, $self->{InvoiceNumber} );
}

sub pay {
    my ( $self, $data ) = @_;
    $data->{PayInvoice} = $self->{InvoiceNumber};
    $self->{kf}->_c( "InsertReceiptPayment", $data );
}

package Net::KashFlow::Payment;
use base 'Net::KashFlow::Base';
sub _this { "Payment" }
__PACKAGE__->mk_accessors(
    qw/
      PayID PayInvoice PayDate PayNote PayMethod PayAccount PayAmount
      /
);


package Net::KashFlow::Supplier;
use base 'Net::KashFlow::Base';
sub _this { "Supplier" }
__PACKAGE__->mk_accessors(qw/SupplierID Code/);


1;    # End of Net::KashFlow

__END__

=pod

=encoding UTF-8

=head1 NAME

Net::KashFlow

=head1 VERSION

version 0.04

=head1 SYNOPSIS

    my $kf = Net::KashFlow->new(username => $u, password => $p);

    my $c = $kf->get_customer($cust_email);
    my $i = $kf->create_invoice({
        InvoiceNumber => time, CustomerID => $c->CustomerID
    });

    $i->add_line({ Quantity => 1, Description => "Widgets", Rate => 100 })

    $i->pay({ PayAmount => 100 });

=head1 NAME

Net::KashFlow - Interact with KashFlow accounting web service

=head1 WARNING

This module is incomplete. It does not implement all of the KashFlow
API. Please find the github repository at
http://github.com/simoncozens/Net-KashFlow and send me pull requests for
your newly-implemented features. Thank you.

=head1 METHODS

=head2 new

Simple constructor - requires "username" and "password" named
parameters.

=head2 get_supplier($id)

Returns a Net::KashFlow::Supplier object for the supplier

=head2 get_supplier_by_id($id)

Returns the Net::KashFlow::Supplier object as specified by the ID

=head2 get_customer($id | $email)

Returns a Net::KashFlow::Customer object for the given customer. If the
parameter passed has an C<@> sign then this is treated as an email
address and the customer looked up email address; otherwise the
customer is looked up by customer code. If no customer is found in the
database, nothing is returned.

=head2 get_customer_by_id($internal_id)

Like C<get_customer>, but works on the internal ID of the customer.

=head2 get_customers

Returns all customers

=head2 create_customer({ Name => "...", Address => "...", ... });

Inserts a new customer into the database, returning a
Net::KashFlow::Customer object.

=head2 get_invoice($your_id)

=head2 get_invoice_by_id($internal_id)

Returns a Net::KashFlow::Invoice object representing the invoice.

=head2 get_invoice_pdf($id)

Returns the URI for a PDF of the specified invoice

=head2 get_invoice_paypal_link($id)

Returns a Paypal payment link for the specified invoice ID.

=head2 get_overdue_invoices

Returns an array of overdue invoices. Each element is a
Net::KashFlow::Invoice object

=head2 get_unpaid_invoices

Returns an array of unpaid invoices. Each element is a
Net::KashFlow::Invoice object

=head2 get_invoices_for_customer($customerID)

Returns an array containing all of the invoices for the specified customer

=head2 create_invoice({ ... })

=head2 delete_invoice($invoice_id)

Delete an invoice. Returns true if invoice deleted

=head2 get_receipt($id)

Returns a Net::KashFlow::Receipt object representing the receipt

=head2 get_receipt_by_id($id)

Returns a Net::KashFlow::Receipt object representing the receipt but
by ID

=head2 get_receipts_for_supplier($id)

Returns an array containing all receipts for the specified supplier.

=head2 create_receipt ({ ... })

Create a new receipt. For details, see

http://accountingapi.com/manual_methods_InsertReceipt.asp

Returns a Net::KashFlow::Receipt object

=head2 get_invoice_payment()

Returns a Net::KashFlow::Payment object for an invoice payment.

    $kf->get_invoice_payment({
        InvoiceNumber => $id
    });

Returns a Net::KashFlow::Payment object for an invoice payment.

=head2 delete_invoice_payment

    $kf->delete_invoice_payment({
        InvoicePaymentNumber => 12345
    })

Deletes a specific invoice payment

Returns 1 if payment deleted

=head2 get_vat_rates();

Returns a list of VAT rates

=head1 Net::KashFlow::Customer

    my $c = $kf->get_customer($email);

    $c->Telephone("+44.500123456");
    $c->update;

    print $c->Address1(), $c->Address2();

Customer objects have accessors as specified by
C<http://accountingapi.com/manual_class_customer.asp> - these accessors
are not "live" in that changes to them are only sent to KashFlow on call
to the C<update> method.

This package also has a C<delete> method to remove the customer from the
database, and an C<invoices> method which returns all the
C<Net::KashFlow::Invoice> objects assigned to this customer.

=head1 Net::KashFlow::Invoice

    my @i = $kf->get_customer($email)->invoices;
    for (@i) { $i->Paid(1); $i->update }

Similarly to Customer, fields found at
http://accountingapi.com/manual_class_invoice.asp

Also:

    $i->add_line({ Quantity => 1, Description => "Widgets", Rate => 100 });
    $i->pay({ PayAmount => 100 });

=head1 Net::KashFlow::Receipt

    my @i = $kf->get_customer($email)->receipts;
    for (@i) { $i->Paid(1); $i->update }

Just like Net::KashFlow::Invoice but for receipts. Fields at
http://accountingapi.com/manual_class_invoice.asp

Also:

    $i->add_line({ Quantity => 1, Description => "Widgets", Rate => 100 });
    $i->pay({ PayAmount => 100 });

=head1 Net::KashFlow::Payment

Payment object. Fields: http://accountingapi.com/manual_class_payment.asp

=head1 AUTHOR

Simon Cozens, C<< <simon at simon-cozens.org> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-net-kashflow at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-KashFlow>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

I am aware that this module is WOEFULLY INCOMPLETE and I'm looking
forward to receiving patches to add new functionality to it. Currently
it does what I want and I don't have much incentive to finish it. :/

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Net::KashFlow

You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-KashFlow>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Net-KashFlow>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Net-KashFlow>

=item * Search CPAN

L<http://search.cpan.org/dist/Net-KashFlow/>

=back

=head1 ACKNOWLEDGEMENTS

Thanks to the UK Free Software Network (http://www.ukfsn.org/) for their
support of this module's development. For free-software-friendly hosting
and other Internet services, try UKFSN.

=head1 COPYRIGHT & LICENSE

Copyright 2009 Simon Cozens.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=head1 AUTHORS

=over 4

=item *

'Simon Cozens <simon at simon-cozens.org>'

=item *

'Jason Clifford <jason@ukfsn.org>'

=item *

'Gavin Henry <ghenry@surevoip.co.uk>'

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Gavin Henry, Simon Cozens, Jason Clifford.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
