アカウント照合
アカウント照合は、Omise transactionレコードが銀行明細書および会計システムと一致することを保証します。このガイドでは、照合プロセス、自動照合、およびベストプラクティスについて説明します。
概要
照合は以下の目的で不可欠です:
- 正確な会計: 財務記録が正確であることを保証
- 不正検出: 不正なtransactionを特定
- コンプライアンス: 規制要件を満たす
- キャッシュフロー管理: 実際の資金と予想資金を追跡
- エラー特定: 処理の問題を早期に発見
- 監査証跡: 完全な財務記録 を維持
主要な概念
- Settlement: 銀行口座に支払われるtransactionのバッチ
- Settlement Date: 資金が銀行に到着する日
- Transaction Date: 顧客の支払いが発生した日
- Net Amount: 手数料控除後のsettlement金額
- Gross Amount: 手数料控除前の総payment金額
Settlementの理解
Settlementサイクル
const omise = require('omise')({
secretKey: 'skey_test_123456789',
});
// 最近のtransfer(settlements)を取得
async function getRecentSettlements(days = 30) {
const endDate = new Date();
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
const transfers = await omise.transfers.list({
from: startDate.toISOString(),
to: endDate.toISOString(),
limit: 100
});
console.log(`Found ${transfers.data.length} settlements`);
transfers.data.forEach(transfer => {
console.log(`Transfer ${transfer.id}:`);
console.log(` Amount: ${transfer.amount / 100} ${transfer.currency.toUpperCase()}`);
console.log(` Fee: ${transfer.fee / 100} ${transfer.currency.toUpperCase()}`);
console.log(` Date: ${new Date(transfer.created).toLocaleDateString()}`);
});
return transfers.data;
}
// settlement合計を計算
function calculateSettlementTotal(settlement) {
return {
gross_amount: settlement.amount,
fee: settlement.fee,
net_amount: settlement.amount - settlement.fee,
currency: settlement.currency,
formatted: {
gross: `${settlement.amount / 100} ${settlement.currency.toUpperCase()}`,
fee: `${settlement.fee / 100} ${settlement.currency.toUpperCase()}`,
net: `${(settlement.amount - settlement.fee) / 100} ${settlement.currency.toUpperCase()}`
}
};
}
// 使用 例
getRecentSettlements(30);
照合プロセス
基本的な照合
- Node.js
- Python
- Ruby
class ReconciliationManager {
constructor() {
this.discrepancies = [];
}
async reconcileWithBankStatement(bankStatement, startDate, endDate) {
// Omise settlementsを取得
const settlements = await this.getOmiseSettlements(startDate, endDate);
// Omise transactionsを取得
const transactions = await this.getOmiseTransactions(startDate, endDate);
// settlementsを銀行エントリーと照合
const matchResults = this.matchSettlements(settlements, bankStatement);
// 照合レポートを生成
const report = this.generateReconciliationReport(
settlements,
transactions,
bankStatement,
matchResults
);
return report;
}
async getOmiseSettlements(startDate, endDate) {
const transfers = await omise.transfers.list({
from: startDate.toISOString(),
to: endDate.toISOString(),
limit: 100
});
return transfers.data.map(t => ({
id: t.id,
date: new Date(t.created),
amount: t.amount - t.fee, // 正味金額
gross: t.amount,
fee: t.fee,
currency: t.currency,
status: t.paid ? 'paid' : 'pending'
}));
}
async getOmiseTransactions(startDate, endDate) {
const transactions = await omise.transactions.list({
from: startDate.toISOString(),
to: endDate.toISOString(),
limit: 1000
});
return transactions.data;
}
matchSettlements(settlements, bankStatement) {
const matched = [];
const unmatchedOmise = [];
const unmatchedBank = [];
const bankEntries = [...bankStatement];
settlements.forEach(settlement => {
const match = this.findMatchingBankEntry(settlement, bankEntries);
if (match) {
matched.push({
settlement: settlement,
bankEntry: match.entry,
confidence: match.confidence
});
// 一致したエントリーを削除
bankEntries.splice(match.index, 1);
} else {
unmatchedOmise.push(settlement);
}
});
unmatchedBank.push(...bankEntries);
return {
matched,
unmatchedOmise,
unmatchedBank,
matchRate: (matched.length / settlements.length * 100).toFixed(2)
};
}
findMatchingBankEntry(settlement, bankEntries) {
for (let i = 0; i < bankEntries.length; i++) {
const entry = bankEntries[i];
// 金額で照合(手数料のわずかな差を許容)
const amountDiff = Math.abs(settlement.amount - entry.amount);
// 日付で照合(数日の差を許容)
const daysDiff = Math.abs(
(settlement.date - new Date(entry.date)) / (1000 * 60 * 60 * 24)
);
if (amountDiff < 100 && daysDiff <= 3) {
const confidence = this.calculateMatchConfidence(
amountDiff,
daysDiff
);
return {
entry: entry,
index: i,
confidence: confidence
};
}
}
return null;
}
calculateMatchConfidence(amountDiff, daysDiff) {
let confidence = 100;
// 金額差に基づいて信頼度を減らす
confidence -= (amountDiff / 100) * 5;
// 日付差に基づいて信頼度を減らす
confidence -= daysDiff * 10;
return Math.max(0, confidence);
}
generateReconciliationReport(settlements, transactions, bankStatement, matchResults) {
return {
period: {
start: settlements[0]?.date,
end: settlements[settlements.length - 1]?.date
},
summary: {
total_settlements: settlements.length,
total_bank_entries: bankStatement.length,
matched: matchResults.matched.length,
unmatched_omise: matchResults.unmatchedOmise.length,
unmatched_bank: matchResults.unmatchedBank.length,
match_rate: matchResults.matchRate + '%'
},
totals: {
omise_total: settlements.reduce((sum, s) => sum + s.amount, 0),
bank_total: bankStatement.reduce((sum, e) => sum + e.amount, 0),
difference: 0
},
matched: matchResults.matched,
discrepancies: {
unmatched_omise: matchResults.unmatchedOmise,
unmatched_bank: matchResults.unmatchedBank
}
};
}
printReconciliationReport(report) {
console.log('\n' + '='.repeat(60));
console.log('照合レポート');
console.log('='.repeat(60));
console.log(`期間: ${report.period.start.toLocaleDateString()} - ${report.period.end.toLocaleDateString()}`);
console.log('\n概要:');
console.log(` Omise Settlements: ${report.summary.total_settlements}`);
console.log(` 銀行エントリー: ${report.summary.total_bank_entries}`);
console.log(` 一致: ${report.summary.matched}`);
console.log(` 一致率: ${report.summary.match_rate}`);
console.log('\n不一致:');
console.log(` 未一致のOmise: ${report.summary.unmatched_omise}`);
console.log(` 未一致の銀行: ${report.summary.unmatched_bank}`);
console.log('='.repeat(60));
}
}
// 使用例
const reconciler = new ReconciliationManager();
const bankStatement = [
{ date: '2024-01-15', amount: 97500, reference: 'OMISE-001' },
{ date: '2024-01-22', amount: 195000, reference: 'OMISE-002' }
];
reconciler.reconcileWithBankStatement(
bankStatement,
new Date('2024-01-01'),
new Date('2024-01-31')
).then(report => {
reconciler.printReconciliationReport(report);
});
import omise
from datetime import datetime, timedelta
from collections import defaultdict
omise.api_secret = 'skey_test_123456789'
class ReconciliationManager:
def __init__(self):
self.discrepancies = []
def reconcile_with_bank_statement(self, bank_statement, start_date, end_date):
"""Omiseレコードを銀行明細書と照合"""
# Omise settlementsを取得
settlements = self.get_omise_settlements(start_date, end_date)
# Omise transactionsを取得
transactions = self.get_omise_transactions(start_date, end_date)
# settlementsを銀行エントリーと照合
match_results = self.match_settlements(settlements, bank_statement)
# レポートを生成
report = self.generate_reconciliation_report(
settlements,
transactions,
bank_statement,
match_results
)
return report
def get_omise_settlements(self, start_date, end_date):
"""Omise settlements(transfers)を取得"""
transfers = omise.Transfer.retrieve(
created={
'gte': start_date.isoformat(),
'lte': end_date.isoformat()
},
limit=100
)
return [
{
'id': t.id,
'date': datetime.fromtimestamp(t.created),
'amount': t.amount - t.fee, # 正味金額
'gross': t.amount,
'fee': t.fee,
'currency': t.currency,
'status': 'paid' if t.paid else 'pending'
}
for t in transfers.data
]
def get_omise_transactions(self, start_date, end_date):
"""Omise transactionsを取得"""
transactions = omise.Transaction.retrieve(
created={
'gte': start_date.isoformat(),
'lte': end_date.isoformat()
},
limit=1000
)
return transactions.data
def match_settlements(self, settlements, bank_statement):
"""settlementsを銀行エントリーと照合"""
matched = []
unmatched_omise = []
unmatched_bank = list(bank_statement)
for settlement in settlements:
match = self.find_matching_bank_entry(settlement, unmatched_bank)
if match:
matched.append({
'settlement': settlement,
'bank_entry': match['entry'],
'confidence': match['confidence']
})
unmatched_bank.remove(match['entry'])
else:
unmatched_omise.append(settlement)
match_rate = (len(matched) / len(settlements) * 100) if settlements else 0
return {
'matched': matched,
'unmatched_omise': unmatched_omise,
'unmatched_bank': unmatched_bank,
'match_rate': f"{match_rate:.2f}"
}
def find_matching_bank_entry(self, settlement, bank_entries):
"""settlementの一致する銀行エントリーを見つける"""
for entry in bank_entries:
# 金額で照合(わずかな差を許容)
amount_diff = abs(settlement['amount'] - entry['amount'])
# 日付で照合(数日の差を許容)
if isinstance(entry['date'], str):
entry_date = datetime.strptime(entry['date'], '%Y-%m-%d')
else:
entry_date = entry['date']
days_diff = abs((settlement['date'] - entry_date).days)
if amount_diff < 100 and days_diff <= 3:
confidence = self.calculate_match_confidence(amount_diff, days_diff)
return {
'entry': entry,
'confidence': confidence
}
return None
def calculate_match_confidence(self, amount_diff, days_diff):
"""一致の信頼度スコアを計算"""
confidence = 100
# 差に基づいて信頼度を減らす
confidence -= (amount_diff / 100) * 5
confidence -= days_diff * 10
return max(0, confidence)
def generate_reconciliation_report(self, settlements, transactions, bank_statement, match_results):
"""包括的な照合レポートを生成"""
omise_total = sum(s['amount'] for s in settlements)
bank_total = sum(e['amount'] for e in bank_statement)
return {
'period': {
'start': settlements[0]['date'] if settlements else None,
'end': settlements[-1]['date'] if settlements else None
},
'summary': {
'total_settlements': len(settlements),
'total_bank_entries': len(bank_statement),
'matched': len(match_results['matched']),
'unmatched_omise': len(match_results['unmatched_omise']),
'unmatched_bank': len(match_results['unmatched_bank']),
'match_rate': match_results['match_rate'] + '%'
},
'totals': {
'omise_total': omise_total,
'bank_total': bank_total,
'difference': omise_total - bank_total
},
'matched': match_results['matched'],
'discrepancies': {
'unmatched_omise': match_results['unmatched_omise'],
'unmatched_bank': match_results['unmatched_bank']
}
}
def print_reconciliation_report(self, report):
"""フォーマットされた照合レポートを印刷"""
print('\n' + '='*60)
print('照合レポート')
print('='*60)
if report['period']['start']:
print(f"期間: {report['period']['start'].strftime('%Y-%m-%d')} - {report['period']['end'].strftime('%Y-%m-%d')}")
print('\n概要:')
print(f" Omise Settlements: {report['summary']['total_settlements']}")
print(f" 銀行エントリー: {report['summary']['total_bank_entries']}")
print(f" 一致: {report['summary']['matched']}")
print(f" 一致率: {report['summary']['match_rate']}")
print('\n合計:')
print(f" Omise合計: {report['totals']['omise_total'] / 100:,.2f} THB")
print(f" 銀行合計: {report['totals']['bank_total'] / 100:,.2f} THB")
print(f" 差額: {report['totals']['difference'] / 100:,.2f} THB")
print('\n不一致:')
print(f" 未一致のOmise: {report['summary']['unmatched_omise']}")
print(f" 未一致の銀行: {report['summary']['unmatched_bank']}")
print('='*60)
def export_reconciliation_csv(self, report, filename='reconciliation_report.csv'):
"""照合レポートをCSVにエクスポート"""
import csv
with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
# 一致したエントリーを書き込み
writer.writerow(['一致したエントリー'])
writer.writerow(['Omise ID', 'Omise日付', 'Omise金額', '銀行日付', '銀行金額', '信頼度'])
for match in report['matched']:
writer.writerow([
match['settlement']['id'],
match['settlement']['date'].strftime('%Y-%m-%d'),
match['settlement']['amount'] / 100,
match['bank_entry']['date'],
match['bank_entry']['amount'] / 100,
f"{match['confidence']:.1f}%"
])
writer.writerow([])
# 未一致のOmiseを書き込み
writer.writerow(['未一致のOmise Settlements'])
writer.writerow(['ID', '日付', '金額'])
for settlement in report['discrepancies']['unmatched_omise']:
writer.writerow([
settlement['id'],
settlement['date'].strftime('%Y-%m-%d'),
settlement['amount'] / 100
])
writer.writerow([])
# 未一致の銀行を書き込み
writer.writerow(['未一致の銀行エントリー'])
writer.writerow(['日付', '金額', '参照'])
for entry in report['discrepancies']['unmatched_bank']:
writer.writerow([
entry['date'],
entry['amount'] / 100,
entry.get('reference', 'N/A')
])
print(f"レポートを{filename}にエクスポートしました")
# 使用例
reconciler = ReconciliationManager()
bank_statement = [
{'date': '2024-01-15', 'amount': 97500, 'reference': 'OMISE-001'},
{'date': '2024-01-22', 'amount': 195000, 'reference': 'OMISE-002'}
]
start_date = datetime(2024, 1, 1)
end_date = datetime(2024, 1, 31)
report = reconciler.reconcile_with_bank_statement(bank_statement, start_date, end_date)
reconciler.print_reconciliation_report(report)
reconciler.export_reconciliation_csv(report)
require 'omise'
require 'date'
require 'csv'
Omise.api_key = 'skey_test_123456789'
class ReconciliationManager
attr_reader :discrepancies
def initialize
@discrepancies = []
end
def reconcile_with_bank_statement(bank_statement, start_date, end_date)
# Omise settlementsを取得
settlements = get_omise_settlements(start_date, end_date)
# Omise transactionsを取得
transactions = get_omise_transactions(start_date, end_date)
# settlementsを銀行エントリーと照合
match_results = match_settlements(settlements, bank_statement)
# レポートを生成
generate_reconciliation_report(
settlements,
transactions,
bank_statement,
match_results
)
end
def get_omise_settlements(start_date, end_date)
transfers = Omise::Transfer.retrieve(
created: {
gte: start_date.iso8601,
lte: end_date.iso8601
},
limit: 100
)
transfers.data.map do |t|
{
id: t.id,
date: Time.at(t.created),
amount: t.amount - t.fee, # 正味金額
gross: t.amount,
fee: t.fee,
currency: t.currency,
status: t.paid ? 'paid' : 'pending'
}
end
end
def get_omise_transactions(start_date, end_date)
transactions = Omise::Transaction.retrieve(
created: {
gte: start_date.iso8601,
lte: end_date.iso8601
},
limit: 1000
)
transactions.data
end
def match_settlements(settlements, bank_statement)
matched = []
unmatched_omise = []
unmatched_bank = bank_statement.dup
settlements.each do |settlement|
match = find_matching_bank_entry(settlement, unmatched_bank)
if match
matched << {
settlement: settlement,
bank_entry: match[:entry],
confidence: match[:confidence]
}
unmatched_bank.delete(match[:entry])
else
unmatched_omise << settlement
end
end
match_rate = settlements.empty? ? 0 : (matched.length.to_f / settlements.length * 100)
{
matched: matched,
unmatched_omise: unmatched_omise,
unmatched_bank: unmatched_bank,
match_rate: format('%.2f', match_rate)
}
end
def find_matching_bank_entry(settlement, bank_entries)
bank_entries.each do |entry|
# 金額で照合
amount_diff = (settlement[:amount] - entry[:amount]).abs
# 日付で照合
entry_date = entry[:date].is_a?(String) ? Date.parse(entry[:date]) : entry[:date]
days_diff = (settlement[:date].to_date - entry_date).abs
if amount_diff < 100 && days_diff <= 3
confidence = calculate_match_confidence(amount_diff, days_diff)
return {
entry: entry,
confidence: confidence
}
end
end
nil
end
def calculate_match_confidence(amount_diff, days_diff)
confidence = 100
confidence -= (amount_diff.to_f / 100) * 5
confidence -= days_diff * 10
[0, confidence].max
end
def generate_reconciliation_report(settlements, transactions, bank_statement, match_results)
omise_total = settlements.sum { |s| s[:amount] }
bank_total = bank_statement.sum { |e| e[:amount] }
{
period: {
start: settlements.first&.dig(:date),
end: settlements.last&.dig(:date)
},
summary: {
total_settlements: settlements.length,
total_bank_entries: bank_statement.length,
matched: match_results[:matched].length,
unmatched_omise: match_results[:unmatched_omise].length,
unmatched_bank: match_results[:unmatched_bank].length,
match_rate: "#{match_results[:match_rate]}%"
},
totals: {
omise_total: omise_total,
bank_total: bank_total,
difference: omise_total - bank_total
},
matched: match_results[:matched],
discrepancies: {
unmatched_omise: match_results[:unmatched_omise],
unmatched_bank: match_results[:unmatched_bank]
}
}
end
def print_reconciliation_report(report)
puts "\n#{'='*60}"
puts '照合レポート'
puts '='*60
if report[:period][:start]
puts "期間: #{report[:period][:start].strftime('%Y-%m-%d')} - #{report[:period][:end].strftime('%Y-%m-%d')}"
end
puts "\n概要:"
puts " Omise Settlements: #{report[:summary][:total_settlements]}"
puts " 銀行エントリー: #{report[:summary][:total_bank_entries]}"
puts " 一致: #{report[:summary][:matched]}"
puts " 一致率: #{report[:summary][:match_rate]}"
puts "\n合計:"
puts " Omise合計: #{format('%.2f', report[:totals][:omise_total] / 100.0)} THB"
puts " 銀行合計: #{format('%.2f', report[:totals][:bank_total] / 100.0)} THB"
puts " 差額: #{format('%.2f', report[:totals][:difference] / 100.0)} THB"
puts "\n不一致:"
puts " 未一致のOmise: #{report[:summary][:unmatched_omise]}"
puts " 未一致の銀行: #{report[:summary][:unmatched_bank]}"
puts '='*60
end
def export_reconciliation_csv(report, filename = 'reconciliation_report.csv')
CSV.open(filename, 'w') do |csv|
# 一致したエントリー
csv << ['一致したエントリー']
csv << ['Omise ID', 'Omise日付', 'Omise金額', '銀行日付', '銀行金額', '信頼度']
report[:matched].each do |match|
csv << [
match[:settlement][:id],
match[:settlement][:date].strftime('%Y-%m-%d'),
match[:settlement][:amount] / 100.0,
match[:bank_entry][:date],
match[:bank_entry][:amount] / 100.0,
format('%.1f%%', match[:confidence])
]
end
csv << []
# 未一致のOmise
csv << ['未一致のOmise Settlements']
csv << ['ID', '日付', '金額']
report[:discrepancies][:unmatched_omise].each do |settlement|
csv << [
settlement[:id],
settlement[:date].strftime('%Y-%m-%d'),
settlement[:amount] / 100.0
]
end
csv << []
# 未一致の銀行
csv << ['未一致の銀行エントリー']
csv << ['日付', '金額', '参照']
report[:discrepancies][:unmatched_bank].each do |entry|
csv << [
entry[:date],
entry[:amount] / 100.0,
entry[:reference] || 'N/A'
]
end
end
puts "レポートを#{filename}にエクスポートしました"
end
end
# 使用例
reconciler = ReconciliationManager.new
bank_statement = [
{ date: '2024-01-15', amount: 97500, reference: 'OMISE-001' },
{ date: '2024-01-22', amount: 195000, reference: 'OMISE-002' }
]
start_date = Date.new(2024, 1, 1)
end_date = Date.new(2024, 1, 31)
report = reconciler.reconcile_with_bank_statement(bank_statement, start_date, end_date)
reconciler.print_reconciliation_report(report)
reconciler.export_reconciliation_csv(report)
自動照合
日次照合スクリプト
<?php
require_once 'vendor/autoload.php';
class DailyReconciliation {
private $omiseKey;
private $emailRecipients;
public function __construct($omiseKey, $emailRecipients) {
$this->omiseKey = $omiseKey;
$this->emailRecipients = $emailRecipients;
}
public function runDailyReconciliation() {
// 昨日のデータを取得
$yesterday = date('Y-m-d', strtotime('-1 day'));
// 銀行明細書を取得(銀行システムから)
$bankStatement = $this->fetchBankStatement($yesterday);
// Omise settlementsを取得
$omiseSettlements = $this->fetchOmiseSettlements($yesterday);
// 照合
$report = $this->reconcile($omiseSettlements, $bankStatement);
// 不一致をチェック
if ($report['discrepancy_count'] > 0) {
$this->sendAlertEmail($report);
}
// レポートを保存
$this->saveReport($report);
return $report;
}
private function fetchOmiseSettlements($date) {
$startDate = date('c', strtotime($date . ' 00:00:00'));
$endDate = date('c', strtotime($date . ' 23:59:59'));
$transfers = OmiseTransfer::retrieve([
'from' => $startDate,
'to' => $endDate,
'limit' => 100
]);
return array_map(function($t) {
return [
'id' => $t['id'],
'date' => date('Y-m-d', $t['created']),
'amount' => $t['amount'] - $t['fee'],
'gross' => $t['amount'],
'fee' => $t['fee']
];
}, $transfers['data']);
}
private function fetchBankStatement($date) {
// ここに銀行API統合を実装
// これ はプレースホルダーです
return [];
}
private function reconcile($omiseSettlements, $bankStatement) {
$matched = 0;
$unmatched = [];
foreach ($omiseSettlements as $settlement) {
$found = false;
foreach ($bankStatement as $entry) {
if ($this->isMatch($settlement, $entry)) {
$matched++;
$found = true;
break;
}
}
if (!$found) {
$unmatched[] = $settlement;
}
}
return [
'date' => date('Y-m-d'),
'total_settlements' => count($omiseSettlements),
'matched' => $matched,
'discrepancy_count' => count($unmatched),
'unmatched' => $unmatched
];
}
private function isMatch($settlement, $bankEntry) {
$amountMatch = abs($settlement['amount'] - $bankEntry['amount']) < 100;
$dateMatch = $settlement['date'] === $bankEntry['date'];
return $amountMatch && $dateMatch;
}
private function sendAlertEmail($report) {
$subject = "⚠️ 照合の不一致 - " . $report['date'];
$message = "{$report['discrepancy_count']}件の未一致のsettlementが見つかりました。\n\n";
foreach ($report['unmatched'] as $settlement) {
$message .= "Settlement {$settlement['id']}: " .
number_format($settlement['amount'] / 100, 2) . " THB\n";
}
foreach ($this->emailRecipients as $recipient) {
mail($recipient, $subject, $message);
}
}
private function saveReport($report) {
$filename = 'reconciliation_' . $report['date'] . '.json';
file_put_contents($filename, json_encode($report, JSON_PRETTY_PRINT));
}
}
// 日次照合を実行
$reconciliation = new DailyReconciliation(
'skey_test_123456789',
['finance@company.com', 'accounting@company.com']
);
$report = $reconciliation->runDailyReconciliation();
echo "照合が完了しました。一致: {$report['matched']}/{$report['total_settlements']}\n";
?>
ベストプラクティス
1. 日次照合の自動化
// 日次照合をスケジュール
const cron = require('node-cron');
class AutomatedReconciliation {
constructor() {
this.reconciler = new ReconciliationManager();
}
scheduleDailyReconciliation() {
// 毎日午前9時に実行
cron.schedule('0 9 * * *', async () => {
console.log('日次照合を開始しています...');
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
try {
const report = await this.runReconciliation(yesterday);
if (report.summary.unmatched_omise > 0 || report.summary.unmatched_bank > 0) {
await this.sendAlertNotification(report);
}
await this.saveReport(report);
console.log('日次照合が完了しました');
} catch (error) {
console.error('照合に失敗しました:', error);
await this.sendErrorNotification(error);
}
});
}
async runReconciliation(date) {
const bankStatement = await this.fetchBankStatement(date);
return await this.reconciler.reconcileWithBankStatement(
bankStatement,
date,
date
);
}
async sendAlertNotification(report) {
// メールまたはSlack通知を送信
console.log('⚠️ 照合に不一致が見つかりました');
}
}
const automated = new AutomatedReconciliation();
automated.scheduleDailyReconciliation();
2. 手数料差異の追跡
def analyze_fee_discrepancies(omise_settlements, bank_statement):
"""手数料関連の不一致を分析"""
discrepancies = []
for settlement in omise_settlements:
# 予想される銀行金額を計算(settlement金額 - Omise手数料)
expected_bank_amount = settlement['gross'] - settlement['fee']
# 対応する銀行エントリーを検索
bank_entry = find_bank_entry_by_date(settlement['date'], bank_statement)
if bank_entry:
actual_bank_amount = bank_entry['amount']
difference = expected_bank_amount - actual_bank_amount
if abs(difference) > 10: # 0.10 THBを超える差
discrepancies.append({
'settlement_id': settlement['id'],
'expected': expected_bank_amount / 100,
'actual': actual_bank_amount / 100,
'difference': difference / 100,
'possible_cause': analyze_difference(difference)
})
return discrepancies
def analyze_difference(difference):
"""差の原因を判断"""
if abs(difference) < 500: # 5 THB未満
return '端数処理の差異'
elif difference > 0:
return '銀行手数料または調整が不足'
else:
return '追加の銀行手数料が請求されました'
3. 監査証跡の維持
class ReconciliationAuditLog
def self.log_reconciliation(report, discrepancies = [])
log_entry = {
timestamp: Time.now.iso8601,
report_date: report[:period][:start].to_s,
match_rate: report[:summary][:match_rate],
total_settlements: report[:summary][:total_settlements],
matched: report[:summary][:matched],
unmatched: report[:summary][:unmatched_omise] + report[:summary][:unmatched_bank],
discrepancies: discrepancies.map { |d| d[:settlement_id] }
}
# 監査ログファイルに保存
File.open('reconciliation_audit.log', 'a') do |f|
f.puts JSON.generate(log_entry)
end
# レポート用にデータベースに も保存
save_to_database(log_entry)
end
def self.save_to_database(entry)
# データベースストレージを実装
end
end
FAQ
アカウントをどのくらいの頻度で照合すべきですか?
取引量の多いビジネスには日次照合をお勧めします。小規模ビジネスは週次または月次で照合できます。自動化された日次照合は、問題を早期に発見するのに役立ちます。
照合の不一致の原因は何ですか?
一般的な原因:
- タイミングの違い(transaction dateとsettlement date)
- 手数料の不一致
- 銀行処理の遅延
- 失敗したtransaction
- 手動調整
- 通貨換算の違い
GrossまたはNet金額を照合すべきですか?
Net金額(手数料控除後)を照合してください。これが銀行口座に表示される金額です。ただし、手数料計算が正しいことを確認するために、Gross金額を個別に追跡してください。
複数日のsettlement遅延にどう対処すべきですか?
TransactionをBankエントリーと照合する際、2〜3日の猶予を許可してください。Settlementは通常1〜2営業日かかりますが、銀行や支払い方法によって異なる場合があります。
Settlementが照合できない場合はどうすればよいですか?
次のように調査してください:
- まだpendingかどうかを確認
- 日付範囲を確認(別の期間にsettleされた可能性)
- 失敗またはreversedされたtransactionを確認
- Omiseサポートに問い合わせ
銀行明細のインポートを自動化できますか?
はい、ほとんどの銀行はAPIまたはファイルエクスポート(CSV、OFX)を提供しており、自動化できます。これらを照合システムと統合して、完全に自動化され た照合を実現します。
照合レコードをどのくらいの期間保管すべきですか?
税務および監査のために、少なくとも7年間照合レコードを保管してください。適切なアクセス制御で安全に保管してください。
不一致を処理する最良の方法は何ですか?
すべての不一致を直ちに文書化し、24時間以内に調査し、解決のログを維持してください。特定のしきい値を超える不一致のアラートを設定してください。
関連リソース
- Balance Overview - Account balanceの理解
- Transaction History - すべてのtransactionを表示
- Creating Transfers - Settlement transfer
- Settlements API - Settlement詳細
- Accounting Integration - 統合ガイド
次のステップ
- 自動日次照合を設定
- 会計ソフトウェアと統合
- 不一致のアラート通知を設定
- Settlementスケジュールを確認
- コンプライアンスのために監査ログを実装