diff --git a/client.go b/client.go
index 72de60c..4ff17ac 100644
--- a/client.go
+++ b/client.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"io"
"io/ioutil"
+ "mime/multipart"
"net/http"
"net/url"
"path"
@@ -154,3 +155,92 @@ func (c *Client) call(ctx context.Context,
return oauth2Token, json.NewDecoder(r).Decode(&res)
}
+
+func (c *Client) upload(ctx context.Context,
+ apiPath string,
+ oauth2Token *oauth2.Token,
+ queryParams url.Values,
+ postBody map[string]string,
+ fileName string,
+ file []byte,
+ res interface{},
+) (*oauth2.Token, error) {
+ // url
+ u, err := url.Parse(c.config.APIEndpoint)
+ if err != nil {
+ return oauth2Token, err
+ }
+ u.Path = path.Join(u.Path, APIPath1, apiPath)
+ u.RawQuery = queryParams.Encode()
+ body := &bytes.Buffer{}
+ // form data
+ mw := multipart.NewWriter(body)
+ fw, err := mw.CreateFormFile("receipt", fileName)
+ if err != nil {
+ return oauth2Token, err
+ }
+ _, err = io.Copy(fw, bytes.NewReader(file))
+ if err != nil {
+ return oauth2Token, err
+ }
+ for k, v := range postBody {
+ err = mw.WriteField(k, v)
+ if err != nil {
+ return oauth2Token, err
+ }
+ }
+ contentType := mw.FormDataContentType()
+ err = mw.Close()
+ if err != nil {
+ return oauth2Token, err
+ }
+ // request
+ req, err := http.NewRequest(http.MethodPost, u.String(), body)
+ if err != nil {
+ return oauth2Token, err
+ }
+ // header Content-Type
+ req.Header.Set("Content-Type", contentType)
+ req = req.WithContext(ctx)
+ tokenSource := c.config.Oauth2.TokenSource(ctx, oauth2Token)
+ httpClient := oauth2.NewClient(ctx, tokenSource)
+ response, err := httpClient.Do(req)
+ if err != nil {
+ return oauth2Token, err
+ }
+ defer response.Body.Close()
+ c.logf("[freee] %s: %s", HeaderXFreeeRequestID, response.Header.Get(HeaderXFreeeRequestID))
+ c.logf("[freee] %s: %v %v%v", response.Status, req.Method, req.URL.Host, req.URL.Path)
+
+ var r io.Reader = response.Body
+ // Parse freee API errors
+ code := response.StatusCode
+ if code >= http.StatusBadRequest {
+ byt, err := ioutil.ReadAll(r)
+ if err != nil {
+ // error occured, but ignored.
+ c.logf("[freee] HTTP response body: %v", err)
+ }
+ res := &Error{
+ StatusCode: code,
+ RawError: string(byt),
+ }
+ // Check if re-authorization is required
+ if code == http.StatusUnauthorized {
+ var e UnauthorizedError
+ if err := json.NewDecoder(bytes.NewReader(byt)).Decode(&e); err != nil {
+ c.logf("[freee] HTTP response body: %v", err)
+ return oauth2Token, res
+ }
+ if e.Code == UnauthorizedCodeInvalidAccessToken ||
+ e.Code == UnauthorizedCodeExpiredAccessToken {
+ res.IsAuthorizationRequired = true
+ }
+ }
+ return oauth2Token, res
+ }
+ if res == nil {
+ return oauth2Token, nil
+ }
+ return oauth2Token, json.NewDecoder(r).Decode(&res)
+}
diff --git a/deals.go b/deals.go
index 3d55224..7c6d86d 100644
--- a/deals.go
+++ b/deals.go
@@ -158,7 +158,7 @@ type DealCreateParamsPayments struct {
// will change when the set of required properties is changed
func NewDealCreateParams(issueDate string, type_ string, companyID int32,
dueDate string, partnerID int32, partnerCode string, refNumber string,
- details *[]DealCreateParamsDetails) *DealCreateParams {
+ receiptIDs []int32, details *[]DealCreateParamsDetails) *DealCreateParams {
this := DealCreateParams{}
this.IssueDate = issueDate
this.Type = type_
@@ -167,6 +167,7 @@ func NewDealCreateParams(issueDate string, type_ string, companyID int32,
this.PartnerID = partnerID
this.PartnerCode = partnerCode
this.RefNumber = refNumber
+ this.ReceiptIDs = receiptIDs
this.Details = details
return &this
}
diff --git a/receipts.go b/receipts.go
new file mode 100644
index 0000000..7eea7c4
--- /dev/null
+++ b/receipts.go
@@ -0,0 +1,85 @@
+package freee
+
+import (
+ "context"
+ "golang.org/x/oauth2"
+ "strconv"
+)
+
+const (
+ APIPathReceipts = "receipts"
+)
+
+type CreateReceiptParams struct {
+ // 事業所ID
+ CompanyID int32 `json:"company_id"`
+ // メモ (255文字以内)
+ Description string `json:"description,omitempty"`
+ // 取引日 (yyyy-mm-dd)
+ IssueDate string `json:"issue_date"`
+ // 証憑ファイル
+ Receipt []byte `json:"receipt"`
+}
+
+// NewReceiptUpdateParams instantiates a new ReceiptUpdateParams object
+// This constructor will assign default values to properties that have it defined,
+// and makes sure properties required by API are set, but the set of arguments
+// will change when the set of required properties is changed
+func NewCreateReceiptParams(companyID int32, description string, issueDate string, receipt []byte) *CreateReceiptParams {
+ this := CreateReceiptParams{}
+ this.CompanyID = companyID
+ this.IssueDate = issueDate
+ this.Description = description
+ this.Receipt = receipt
+ return &this
+}
+
+type ReceiptResponse struct {
+ Receipt Receipt `json:"receipt"`
+}
+
+type Receipt struct {
+ // 証憑ID
+ ID int32 `json:"id"`
+ // ステータス(unconfirmed:確認待ち、confirmed:確認済み、deleted:削除済み、ignored:無視)
+ Status string `json:"status"`
+ // メモ
+ Description string `json:"description,omitempty"`
+ // MIMEタイプ
+ MimeType string `json:"mime_type"`
+ // 発生日
+ IssueDate string `json:"issue_date,omitempty"`
+ // アップロード元種別
+ Origin string `json:"origin"`
+ // 作成日時(ISO8601形式)
+ CreatedAt string `json:"created_at"`
+ // ファイルのダウンロードURL(freeeにログインした状態でのみ閲覧可能です。)
file_srcは廃止予定の属性になります。
file_srcに替わり、証憑ファイルのダウンロード APIをご利用ください。
証憑ファイルのダウンロードAPIを利用することで、以下のようになります。